├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── RELEASE.md ├── code-coverage.sh ├── config ├── config.go └── config_test.go ├── doc ├── git-conflict.1 ├── git-conflict.html ├── git-octopus.1 └── git-octopus.html ├── git-octopus.go ├── git ├── git.go └── git_test.go ├── run ├── basics_test.go ├── context_test.go ├── edge_cases_test.go ├── matcher.go ├── matcher_test.go ├── no_commit_test.go └── run.go ├── src ├── doc │ ├── git-conflict.1.txt │ └── git-octopus.1.txt ├── git-apply-conflict-resolution ├── git-conflict ├── git-octopus └── lib │ ├── common │ ├── git-merge-octopus-fork.sh │ └── hash-conflict ├── test ├── empty_command_test │ ├── Dockerfile │ └── test.sh ├── exclude_pattern_config_test │ ├── Dockerfile │ └── test.sh ├── no_commit_config_test │ ├── Dockerfile │ └── test.sh ├── no_commit_detached_test │ ├── Dockerfile │ └── test.sh ├── no_commit_test │ ├── Dockerfile │ └── test.sh ├── octopus_failed_test │ ├── Dockerfile │ └── test.sh ├── pattern_config_test │ ├── Dockerfile │ └── test.sh ├── record_conflicts_test │ ├── Dockerfile │ └── test.sh ├── run_all.sh ├── run_test.sh ├── simple_test │ ├── Dockerfile │ └── test.sh ├── single_parent_merge_test │ ├── Dockerfile │ └── test.sh └── test_utils.go └── vendor ├── github.com └── stretchr │ └── testify │ ├── LICENCE.txt │ ├── LICENSE │ ├── assert │ ├── assertion_forward.go │ ├── assertions.go │ ├── doc.go │ ├── errors.go │ ├── forward_assertions.go │ └── http_assertions.go │ ├── doc.go │ ├── http │ ├── doc.go │ ├── test_response_writer.go │ └── test_round_tripper.go │ ├── mock │ ├── doc.go │ └── mock.go │ ├── require │ ├── doc.go │ ├── forward_requirements.go │ ├── require.go │ ├── require_forward.go │ └── requirements.go │ ├── suite │ ├── doc.go │ ├── interfaces.go │ └── suite.go │ └── vendor │ └── github.com │ ├── davecgh │ └── go-spew │ │ └── spew │ │ ├── bypass.go │ │ ├── bypasssafe.go │ │ ├── common.go │ │ ├── config.go │ │ ├── doc.go │ │ ├── dump.go │ │ ├── format.go │ │ └── spew.go │ ├── pmezard │ └── go-difflib │ │ └── difflib │ │ └── difflib.go │ └── stretchr │ └── objx │ ├── accessors.go │ ├── constants.go │ ├── conversions.go │ ├── doc.go │ ├── map.go │ ├── mutations.go │ ├── security.go │ ├── tests.go │ ├── type_specific_codegen.go │ └── value.go └── manifest /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | test/**/bin 3 | 4 | .idea/ 5 | *.iml 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.7.x 5 | 6 | script: 7 | - git --version 8 | # To run tests while developing, use 'go test ./...' 9 | - ./code-coverage.sh 10 | - test -z "$(gofmt -l **/*.go 2>&1)" 11 | 12 | after_success: 13 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | prefix = /usr/local 2 | bindir = $(prefix)/bin 3 | datarootdir = $(prefix)/share 4 | mandir = $(datarootdir)/man 5 | man1dir = $(mandir)/man1 6 | docdir = $(datarootdir)/doc/git-doc 7 | htmldir = $(docdir) 8 | 9 | cat_scripts = cat $(2) $(3) $(4) $(5) > bin/$(1) \ 10 | && chmod +x bin/$(1) 11 | 12 | generate_docs = asciidoc -d manpage --out-file=doc/$(1).html src/doc/$(1).1.txt \ 13 | && a2x -f manpage src/doc/$(1).1.txt --no-xmllint --destination-dir=doc 14 | 15 | install_docs = cp -f doc/$(1).1 $(man1dir)/$(1).1 \ 16 | && cp -f doc/$(1).html $(htmldir) 17 | 18 | fmt: 19 | gofmt -w **/*.go 20 | 21 | build: 22 | @mkdir -p bin 23 | $(call cat_scripts,git-octopus,src/lib/common,src/lib/git-merge-octopus-fork.sh,src/git-octopus) 24 | $(call cat_scripts,git-conflict,src/lib/common,src/lib/hash-conflict,src/git-conflict) 25 | $(call cat_scripts,git-apply-conflict-resolution,src/lib/common,src/lib/hash-conflict,src/git-apply-conflict-resolution) 26 | @echo 'Build success' 27 | 28 | build-docs: 29 | @mkdir -p doc 30 | $(call generate_docs,git-octopus) 31 | $(call generate_docs,git-conflict) 32 | 33 | install-bin: build 34 | @mkdir -p $(bindir) 35 | @cp -f bin/git-octopus $(bindir) && echo 'Installing $(bindir)/git-octopus' 36 | @cp -f bin/git-conflict $(bindir) && echo 'Installing $(bindir)/git-conflict' 37 | @cp -f bin/git-apply-conflict-resolution $(bindir) && echo 'Installing $(bindir)/git-apply-conflict-resolution' 38 | 39 | install-docs: 40 | @echo 'Installing documentation' 41 | @mkdir -p $(htmldir) 42 | @mkdir -p $(man1dir) 43 | $(call install_docs,git-octopus) 44 | $(call install_docs,git-conflict) 45 | 46 | install: install-bin install-docs 47 | 48 | uninstall: 49 | rm $(bindir)/git-octopus 50 | rm $(bindir)/git-conflict 51 | rm $(bindir)/git-apply-conflict-resolution 52 | rm $(man1dir)/git-octopus.1 53 | rm $(man1dir)/git-conflict.1 54 | rm $(htmldir)/git-octopus.html 55 | rm $(htmldir)/git-conflict.html 56 | 57 | go-build = GOOS=$(1) GOARCH=$(2) go build -o git-octopus-$(1)-$(2)-2.0.beta3 58 | 59 | go-cross-compile: 60 | $(call go-build,darwin,386) 61 | $(call go-build,darwin,amd64) 62 | $(call go-build,freebsd,386) 63 | $(call go-build,freebsd,amd64) 64 | $(call go-build,linux,386) 65 | $(call go-build,linux,amd64) 66 | $(call go-build,windows,386) 67 | $(call go-build,windows,amd64) 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/lesfurets/git-octopus.svg?branch=master)](https://travis-ci.org/lesfurets/git-octopus) 2 | [![codecov](https://codecov.io/gh/lesfurets/git-octopus/branch/master/graph/badge.svg)](https://codecov.io/gh/lesfurets/git-octopus) 3 | 4 | # git-octopus 5 | >The continuous merge workflow is meant for continuous integration/delivery and is based on feature branching. git-octopus provides git commands to implement it. 6 | 7 | ## Installation 8 | 9 | ### Requirements 10 | Requires git >= 1.8 11 | 12 | You need to have a command `shasum` in the path. This is the case on most unix based systems. If you're on Windows/Cygwin you may have to install it. 13 | 14 | ### Homebrew 15 | If you know and use [Homebrew](http://brew.sh), you just need to do: 16 | ```bash 17 | brew update 18 | brew install git-octopus 19 | ``` 20 | ### RPM package manager 21 | 22 | The packaging is temporarily hosted on [Fedora copr](https://copr.fedorainfracloud.org/coprs/baitaand/git-octopus/build/484097/): 23 | 24 | ```bash 25 | dnf copr enable baitaand/git-octopus 26 | dnf install git-octopus 27 | ``` 28 | 29 | ### From sources 30 | Download the latest [release](https://github.com/lesfurets/git-octopus/releases/latest) or clone this repository. Go into the directory and type 31 | ```bash 32 | make install 33 | ``` 34 | 35 | Make sure the installation works 36 | ```bash 37 | git octopus -v 38 | ``` 39 | 40 | >We respect [semver](http://semver.org/) 41 | 42 | ## What you'll find 43 | Two additionnal git commands : 44 | 45 | ### git octopus 46 | Extends `git merge` with branch naming patterns. For instance 47 | ``` 48 | git octopus features/* 49 | ``` 50 | Merges all branches named features/ into the current branch. 51 | See [git-octopus(1)](http://lesfurets.github.io/git-octopus/doc/git-octopus.html). 52 | 53 | ### git conflict 54 | Allows you to record conflicts resolutions that `git octopus` can reuse. 55 | Conflicts resolutions are standard refs so they can be pushed/fetched. 56 | See the conflicts management section bellow and [git-conflict(1)](http://lesfurets.github.io/git-octopus/doc/git-conflict.html). 57 | 58 | ## The Continuous Merge 59 | 60 | ### What is it all about ? 61 | Feature branching and continuous integration don't live well together. The idea of this project is to reconcile those two by using the mighty power of git. 62 | 63 | I gave a talk about why and how to use it at Devoxx France 2015, but it's in french ;) https://www.youtube.com/watch?v=8P0vyWIyAXM 64 | 65 | ### The branching model 66 | The simpliest form of the model is to have a mainline branch, let's call it `master`, and feature branches on top of that master. In a continuous delivery workflow you won't need more than that. 67 | 68 | * The `master` branch, or however you call it, is in a ready-to-ship state. Nobody commits on it. 69 | * A feature branch is a change, as small as possible, that can bring the `master` from a ready-to-ship state to an other. 70 | 71 | This means that all the work is done in feature branches. Don't be afraid to have many, one branch per developer is fine. Keep feature branches independent from each other, that's the key for having a fluent delivery pipe. 72 | 73 | ### The workflow 74 | `git octopus` allows you to merge all you feature branches together at any moment so you can have an assembly of all the work that is going on and finally do a continuous integration job on that merge. here's how it works : 75 | 76 | A developer pushes a change on his feature branch. There is a job in your continuous integration system that will trigger and do this bash command : 77 | 78 | ```bash 79 | git octopus origin/features/* origin/master && git push origin +HEAD:octopus 80 | ``` 81 | This job computes a merge with all feature branches and the master, and then pushes the result on a branch `octopus` on origin. 82 | The new merge commit on `octopus` will now trigger an other job that will build and deploy this merge on your test servers etc ... 83 | Note that the octopus merge is not kept in any history line. The next push on any feature branch will trigger the build of a new merge that will be forced push again on `octopus`. 84 | 85 | Once a feature branch is validated on your test environment, you can merge it on master. 86 | 87 | ### Managing conflicts 88 | If `git-octopus` fails, it will do a diagnostic sequence to figure out the conflict precisely. It can lead to two cases : 89 | 90 | * A conflict has been found 91 | 92 | 1. Ask yourself if you could avoid that conflict. Rewriting the history is possible as long as you're alone working on the branch. 93 | 94 | 2. Use [git-conflict](http://lesfurets.github.io/git-octopus/doc/git-conflict.html) to record a resolution and push it to origin. See the documentation for more details. 95 | 96 | 3. Consider to remove one of the conflicting branches from the continuous integration (I.E. rename the branch so it won't get caught in the merge) and wait for the other to be merged in `master`. Then you'll be able to update and resolve the conflict. 97 | 98 | 4. Rebase one branch on top of the other (depending on which one you want to ship first). This has to be the last resort because you'll loose branches independency. 99 | 100 | * No conflict found 101 | 102 | 1. Someone else might breaks the merge, look at previous octopus job executions. 103 | 104 | 2. You felt in a complex case. There are ongoing works to prevent that from happening but for the moment this might happen. Don't hesitate to open an issue ! 105 | 106 | ## Community 107 | 108 | We have a [Google Group](https://groups.google.com/forum/#!forum/git-octopus), feel free to come and discuss with us. You can also send an email to git-octopus@googlegroups.com. 109 | 110 | [![Analytics](https://ga-beacon.appspot.com/UA-79856083-1/README.md?pixel&useReferrer)](https://github.com/igrigorik/ga-beacon) 111 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Source 2 | 3 | ## Bump the version 4 | This is not automated. Search references of the current version number in the `src` folder and change them. 5 | 6 | ## Generate the documentation 7 | Note that even if there's no documentation change, this is still needed because the version number is visible in the man doc. 8 | 9 | Run `make build-docs install-docs` (requires asciidoc to be installed). Make sure the documentation looks good. 10 | 11 | ## Commit & push 12 | Commit the version bump and the generated documentation. 13 | 14 | Push to `master` and `gh-pages` branches. 15 | 16 | ## Tag and patch note 17 | Create a [release on github](https://github.com/lesfurets/git-octopus/releases). Write a patch note and publish it. This will tag the current master. 18 | 19 | # Homebrew 20 | `git-octopus` is part of the homebrew/core tap. Retrieve the tap installation path with 21 | ``` 22 | brew tap-info homebrew/core 23 | ``` 24 | git-octopus's formula is in `Formula/git-octopus.rb` 25 | 26 | Here's the guidelines : 27 | * [CONTRIBUTING.md of homebrew/core tap](https://github.com/homebrew/homebrew-core/blob/master/.github/CONTRIBUTING.md) 28 | * [Formula Cookbook](https://github.com/Homebrew/brew/blob/master/share/doc/homebrew/Formula-Cookbook.md) 29 | 30 | # RPM - Fedora/RHEL 31 | ## Prerequisites 32 | 33 | Install mock and add current user to mock group: 34 | 35 | sudo yum install mock 36 | sudo usermod -a -G mock $(id -u -n) 37 | 38 | Clone the spec file project (the project contains the spec file needed to build the rpm) 39 | 40 | git clone https://github.com/danoliv/git-octopus-spec.git 41 | 42 | ## Update the spec file 43 | 44 | **You should modify the spec file following the official guidelines: [https://fedoraproject.org/wiki/Packaging:Guidelines](https://fedoraproject.org/wiki/Packaging:Guidelines)** 45 | 46 | Check the last available version of git-octopus from the official repo: [https://github.com/lesfurets/git-octopus/releases/latest](https://github.com/lesfurets/git-octopus/releases/latest) 47 | 48 | Update the Version tag of the spec file to match the latest version, set the Release number to 1 for a new version, increase it if it is only a packaging modification: 49 | 50 | Name: git-octopus 51 | Version: 1.4 52 | Release: 1%{?dist} 53 | Summary: Git commands for continuous delivery 54 | 55 | Update the changelog in the spec files: 56 | 57 | %changelog 58 | * Tue Dec 06 2016 Andrea Baita - 1.4-2 59 | - added documentation build, updated build requires 60 | 61 | * Wed Nov 30 2016 Andrea Baita - 1.4-1 62 | - Packaging of version 1.4. 63 | 64 | * Thu Nov 17 2016 Xavier Bachelot - 1.3-1 65 | - Initial package. 66 | 67 | ## Build the RPM 68 | 69 | retrieve the tarball, the file will be put into `~/rpmbuild/SOURCES` (will create a directory if not exists) 70 | 71 | spectool -g -R git-octopus.spec 72 | 73 | build the source rpm, the file will be put into `~/rpmbuild/SRPMS/` (will create a directory if not exists): 74 | 75 | rpmbuild -bs git-octopus.spec 76 | 77 | finally build the rpm, by passing the src.rpm file created in the previous step, indicate a configuration to use from `/etc/mock/`, without the path prefix and the .cfg suffix: 78 | 79 | mock -r ~/rpmbuild/SRPMS/git-octopus-..src.rpm 80 | 81 | example: 82 | 83 | mock -r epel-6-x86_64 ~/rpmbuild/SRPMS/git-octopus-1.4-2.el6.src.rpm 84 | 85 | the results will be usually available in the directory: `/var/lib/mock//result` (check the output of mock command) 86 | 87 | **If the rpm build fails the spec there could have been some incompatible modification on the code, the spec file should be updated accordingly.** 88 | 89 | ## Test the new package 90 | 91 | check the rpm by compiling and installing in the local machine 92 | 93 | mvn clean install 94 | sudo yum install 95 | 96 | check the git octopus version 97 | 98 | git octopus -v 99 | 100 | ## Make a pull request 101 | 102 | Please make a pull request following the github guide: https://guides.github.com/activities/forking/ -------------------------------------------------------------------------------- /code-coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | echo "" > coverage.txt 5 | 6 | for d in $(go list ./... | grep -v vendor); do 7 | go test -race -coverprofile=profile.out -covermode=atomic $d 8 | if [ -f profile.out ]; then 9 | cat profile.out >> coverage.txt 10 | rm profile.out 11 | fi 12 | done -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "github.com/lesfurets/git-octopus/git" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | type OctopusConfig struct { 12 | PrintVersion bool 13 | DoCommit bool 14 | ChunkSize int 15 | ExcludedPatterns []string 16 | Patterns []string 17 | } 18 | 19 | type excluded_patterns []string 20 | 21 | func (e *excluded_patterns) String() string { 22 | return strings.Join(*e, ",") 23 | } 24 | 25 | func (e *excluded_patterns) Set(value string) error { 26 | *e = append(*e, value) 27 | return nil 28 | } 29 | 30 | func GetOctopusConfig(repo *git.Repository, args []string) (*OctopusConfig, error) { 31 | 32 | var printVersion, noCommitArg, commitArg bool 33 | var chunkSizeArg int 34 | var excludedPatternsArg excluded_patterns 35 | 36 | var commandLine = flag.NewFlagSet("git-octopus", flag.ExitOnError) 37 | commandLine.BoolVar(&printVersion, "v", false, "prints the version of git-octopus.") 38 | commandLine.BoolVar(&noCommitArg, "n", false, "leaves the repository back to HEAD.") 39 | commandLine.BoolVar(&commitArg, "c", false, "Commit the resulting merge in the current branch.") 40 | commandLine.IntVar(&chunkSizeArg, "s", 0, "do the octopus by chunk of n branches.") 41 | commandLine.Var(&excludedPatternsArg, "e", "exclude branches matching the pattern.") 42 | 43 | commandLine.Parse(args) 44 | 45 | var configCommit bool 46 | 47 | rawConfigCommit, err := repo.Git("config", "octopus.commit") 48 | 49 | if err != nil { 50 | configCommit = true 51 | } else { 52 | configCommit, err = strconv.ParseBool(rawConfigCommit) 53 | if err != nil { 54 | return nil, errors.New("Config octopus.commit should be boolean. Given \"" + rawConfigCommit + "\"") 55 | } 56 | } 57 | 58 | if commitArg { 59 | configCommit = true 60 | } 61 | 62 | if noCommitArg { 63 | configCommit = false 64 | } 65 | 66 | configExcludedPatterns, _ := repo.Git("config", "--get-all", "octopus.excludePattern") 67 | 68 | var excludedPatterns []string 69 | 70 | if len(configExcludedPatterns) > 0 { 71 | excludedPatterns = strings.Split(configExcludedPatterns, "\n") 72 | } 73 | 74 | if len(excludedPatternsArg) > 0 { 75 | excludedPatterns = excludedPatternsArg 76 | } 77 | 78 | configPatterns, _ := repo.Git("config", "--get-all", "octopus.pattern") 79 | 80 | var patterns []string 81 | 82 | if len(configPatterns) > 0 { 83 | patterns = strings.Split(configPatterns, "\n") 84 | } 85 | 86 | if commandLine.NArg() > 0 { 87 | patterns = commandLine.Args() 88 | } 89 | 90 | return &OctopusConfig{ 91 | PrintVersion: printVersion, 92 | DoCommit: configCommit, 93 | ChunkSize: chunkSizeArg, 94 | ExcludedPatterns: excludedPatterns, 95 | Patterns: patterns, 96 | }, nil 97 | } 98 | -------------------------------------------------------------------------------- /config/config_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/lesfurets/git-octopus/git" 5 | "github.com/lesfurets/git-octopus/test" 6 | "github.com/stretchr/testify/assert" 7 | "testing" 8 | ) 9 | 10 | func createTestRepo() *git.Repository { 11 | dir := test.CreateTempDir() 12 | 13 | repo := git.Repository{Path: dir} 14 | 15 | repo.Git("init") 16 | 17 | return &repo 18 | } 19 | 20 | func TestDoCommit(t *testing.T) { 21 | repo := createTestRepo() 22 | defer test.Cleanup(repo) 23 | 24 | // GIVEN no config, no option 25 | // WHEN 26 | octopusConfig, err := GetOctopusConfig(repo, nil) 27 | 28 | // THEN doCommit should be true 29 | assert.True(t, octopusConfig.DoCommit) 30 | assert.Nil(t, err) 31 | 32 | // GIVEN config to false, no option 33 | repo.Git("config", "octopus.commit", "false") 34 | // WHEN 35 | octopusConfig, err = GetOctopusConfig(repo, nil) 36 | 37 | // THEN doCommit should be false 38 | assert.False(t, octopusConfig.DoCommit) 39 | assert.Nil(t, err) 40 | 41 | // Config to 0, no option. doCommit should be true 42 | repo.Git("config", "octopus.commit", "0") 43 | octopusConfig, err = GetOctopusConfig(repo, nil) 44 | 45 | assert.False(t, octopusConfig.DoCommit) 46 | assert.Nil(t, err) 47 | 48 | // GIVEN config to false, -c option true 49 | repo.Git("config", "octopus.commit", "false") 50 | // WHEN 51 | octopusConfig, err = GetOctopusConfig(repo, []string{"-c"}) 52 | 53 | // THEN doCommit should be true 54 | assert.True(t, octopusConfig.DoCommit) 55 | assert.Nil(t, err) 56 | 57 | // GIVEN config to true, -n option true 58 | repo.Git("config", "octopus.commit", "true") 59 | // WHEN 60 | octopusConfig, err = GetOctopusConfig(repo, []string{"-n"}) 61 | 62 | // THEN doCommit should be false 63 | assert.False(t, octopusConfig.DoCommit) 64 | assert.Nil(t, err) 65 | } 66 | 67 | func TestChunkMode(t *testing.T) { 68 | repo := createTestRepo() 69 | defer test.Cleanup(repo) 70 | 71 | // GIVEN No option 72 | // WHEN 73 | octopusConfig, err := GetOctopusConfig(repo, nil) 74 | 75 | // THEN chunkSize should be 0 76 | assert.Equal(t, 0, octopusConfig.ChunkSize) 77 | assert.Nil(t, err) 78 | 79 | // GIVEN option -s 5 80 | // WHEN 81 | octopusConfig, err = GetOctopusConfig(repo, []string{"-s", "5"}) 82 | 83 | // THEN chunkSize should be 5 84 | assert.Equal(t, 5, octopusConfig.ChunkSize) 85 | assert.Nil(t, err) 86 | } 87 | 88 | func TestExcludedPatterns(t *testing.T) { 89 | repo := createTestRepo() 90 | defer test.Cleanup(repo) 91 | 92 | // GIVEN no config, no option 93 | // WHEN 94 | octopusConfig, err := GetOctopusConfig(repo, nil) 95 | 96 | // THEN excludedPatterns should be empty 97 | assert.Empty(t, octopusConfig.ExcludedPatterns) 98 | assert.Nil(t, err) 99 | 100 | // GIVEN excludePattern config, no option 101 | repo.Git("config", "octopus.excludePattern", "excluded/*") 102 | repo.Git("config", "--add", "octopus.excludePattern", "excluded_branch") 103 | // WHEN 104 | octopusConfig, err = GetOctopusConfig(repo, nil) 105 | 106 | // THEN excludedPatterns should be set 107 | assert.Equal(t, []string{"excluded/*", "excluded_branch"}, octopusConfig.ExcludedPatterns) 108 | assert.Nil(t, err) 109 | 110 | // GIVEN excludePattern config (from previous assertion), option given 111 | // WHEN 112 | octopusConfig, err = GetOctopusConfig(repo, []string{"-e", "override_excluded"}) 113 | 114 | // THEN option should take precedence 115 | assert.Equal(t, []string{"override_excluded"}, octopusConfig.ExcludedPatterns) 116 | assert.Nil(t, err) 117 | } 118 | 119 | func TestPatterns(t *testing.T) { 120 | repo := createTestRepo() 121 | defer test.Cleanup(repo) 122 | 123 | // GIVEN no config, no option 124 | // WHEN 125 | octopusConfig, err := GetOctopusConfig(repo, nil) 126 | 127 | // THEN excludedPatterns should be empty 128 | assert.Empty(t, octopusConfig.Patterns) 129 | assert.Nil(t, err) 130 | 131 | // GIVEN config, no argument. 132 | repo.Git("config", "octopus.pattern", "test") 133 | repo.Git("config", "--add", "octopus.pattern", "test2") 134 | // WHEN 135 | octopusConfig, err = GetOctopusConfig(repo, nil) 136 | 137 | // THEN patterns should be set 138 | assert.Equal(t, []string{"test", "test2"}, octopusConfig.Patterns) 139 | assert.Nil(t, err) 140 | 141 | // GIVEN config (from previous assertion), argument given 142 | // WHEN 143 | octopusConfig, err = GetOctopusConfig(repo, []string{"arg1", "arg2"}) 144 | 145 | // THEN arguments should take precedence 146 | assert.Equal(t, []string{"arg1", "arg2"}, octopusConfig.Patterns) 147 | assert.Nil(t, err) 148 | } 149 | -------------------------------------------------------------------------------- /doc/git-conflict.1: -------------------------------------------------------------------------------- 1 | '\" t 2 | .\" Title: git-conflict 3 | .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] 4 | .\" Generator: DocBook XSL Stylesheets v1.79.1 5 | .\" Date: 11/18/2016 6 | .\" Manual: git-octopus suit Manual 7 | .\" Source: git-octopus 1.4 8 | .\" Language: English 9 | .\" 10 | .TH "GIT\-CONFLICT" "1" "11/18/2016" "git\-octopus 1\&.4" "git\-octopus suit Manual" 11 | .\" ----------------------------------------------------------------- 12 | .\" * Define some portability stuff 13 | .\" ----------------------------------------------------------------- 14 | .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15 | .\" http://bugs.debian.org/507673 16 | .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html 17 | .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18 | .ie \n(.g .ds Aq \(aq 19 | .el .ds Aq ' 20 | .\" ----------------------------------------------------------------- 21 | .\" * set default formatting 22 | .\" ----------------------------------------------------------------- 23 | .\" disable hyphenation 24 | .nh 25 | .\" disable justification (adjust text to left margin only) 26 | .ad l 27 | .\" ----------------------------------------------------------------- 28 | .\" * MAIN CONTENT STARTS HERE * 29 | .\" ----------------------------------------------------------------- 30 | .SH "NAME" 31 | git-conflict \- record conflict resolution\&. 32 | .SH "SYNOPSIS" 33 | .sp 34 | .nf 35 | \fIgit conflict\fR 36 | \fIgit conflict\fR \-\-continue | \-\-abort | \-l 37 | .fi 38 | .SH "DESCRIPTION" 39 | .sp 40 | \fIgit conflict\fR records a conflict resolution between two branches that \fIgit octopus\fR will be able to reuse\&. 41 | .sp 42 | Start the recording process between the current branch and an other branch with 43 | .sp 44 | .if n \{\ 45 | .RS 4 46 | .\} 47 | .nf 48 | $ git conflict an_other_branch 49 | .fi 50 | .if n \{\ 51 | .RE 52 | .\} 53 | .sp 54 | This will set the index and the working tree in the conflict state\&. Once you\(cqve resolved it using \fIgit add\fR, run \fIgit conflict \-\-continue\fR to record the resolution\&. It will print a ref that points to the resolution and resets the repository back to HEAD\&. 55 | .sp 56 | .if n \{\ 57 | .RS 4 58 | .\} 59 | .nf 60 | $ git conflict \-\-continue 61 | Conflict resolution created: refs/conflicts/75dd2c142264373c96a3341b2f9fb57bec7a7b7d 62 | .fi 63 | .if n \{\ 64 | .RE 65 | .\} 66 | .sp 67 | So \fIgit conflict\fR doesn\(cqt write history in your current branch\&. 68 | .sp 69 | \fIgit conflict \-\-continue\fR fails if a resolution for the same conflict already exists\&. To override a resolution you will need to delete the ref with \fIgit update\-ref \-d \fR before recording the new one and possibly force the push\&. 70 | .sp 71 | If you\(cqre satisfied with the resolution you can push it using a simple \fIgit push\fR\&. 72 | .sp 73 | .if n \{\ 74 | .RS 4 75 | .\} 76 | .nf 77 | $ git push origin refs/conflicts/75dd2c142264373c96a3341b2f9fb57bec7a7b7d 78 | .fi 79 | .if n \{\ 80 | .RE 81 | .\} 82 | .SH "OPTIONS" 83 | .PP 84 | \-l 85 | .RS 4 86 | List all resolutions 87 | .RE 88 | .PP 89 | \-\-abort 90 | .RS 4 91 | Abort the current conflict resolution process, and try to reconstruct the pre\-merge state\&. 92 | .sp 93 | If there were uncommitted worktree changes present when the merge started, git conflict \-\-abort will in some cases be unable to reconstruct these changes\&. It is therefore recommended to always commit or stash your changes before running git conflict\&. 94 | .RE 95 | .PP 96 | \-\-continue 97 | .RS 4 98 | Record the index state as the resolution of the conflict and prints its ref\&. The index and the working tree are reset back to HEAD\&. 99 | .RE 100 | .SH "INTERNALS" 101 | .sp 102 | The first thing to know is that a conflict ref, for instance refs/conflicts/75dd2c142264373c96a3341b2f9fb57bec7a7b7d, is a standard commit\&. You can inspect it with \fIgit show\fR or any logger tool\&. It has an author and dates etc\&... 103 | .sp 104 | The sha1 in the ref is a hash of the content of the conflict that has been resolved\&. This allows resolutions to be found easily by other commands given a conflict state by simply hashing the conflict content and looking for a refs/conflict/ of the same name\&. A resolution is not related to a particular branch, it can be applied in any circumstances that produced the same conflict\&. This means that you can continue to commit on your branches, you can rebase them, rename them anyhow\&. As long as the same conflict is produced, the resolution can be retrieved\&. 105 | .SH "LIMITATIONS" 106 | .sp 107 | Currently, when reapplying a resolution, it will only look at both modified files\&. So conflicts with renames, mod changes and deletions are not handled\&. In addition, If you changed something in a file that wasn\(cqt in conflict state, it won\(cqt be reapply neither\&. 108 | .sp 109 | Managing conflicts with more than a single branch is subtle\&. Let\(cqs say I\(cqm on a local branch topic_mine and I have two different conflicts with topic_arya and topic_eddard 110 | .sp 111 | .if n \{\ 112 | .RS 4 113 | .\} 114 | .nf 115 | A\-\-\-B\-\-\-C topic_mine 116 | / 117 | D\-\-\-E\-\-\-F\-\-\-G topic_eddard 118 | \e 119 | H\-\-\-I topic_arya 120 | .fi 121 | .if n \{\ 122 | .RE 123 | .\} 124 | .sp 125 | I can write two resolutions 126 | .sp 127 | .if n \{\ 128 | .RS 4 129 | .\} 130 | .nf 131 | $ git conflict topic_arya 132 | \&.\&.\&. 133 | $ git conflict \-\-continue 134 | Conflict resolution created: refs/conflicts/9a4b40f6ec56260618b820e19c8bd734fbfb744d 135 | $ git conflict topic_eddard 136 | \&.\&.\&. 137 | git conflict \-\-continue 138 | Conflict resolution created: refs/conflicts/8b3d16d22d0138ddbe6a1cd1a4cb9abf516a4609 139 | .fi 140 | .if n \{\ 141 | .RE 142 | .\} 143 | .sp 144 | If I run \fIgit octopus topic_*\fR on my current branch (topic_mine), it will starts by merging topic_arya (branches are sorted alphabeticaly), will find the first resolution and then apply changes from topic_eddard, find the 2nd resolution and end up with a success\&. Let\(cqs push ! 145 | .sp 146 | .if n \{\ 147 | .RS 4 148 | .\} 149 | .nf 150 | $ git push origin refs/conflicts/9a4b40f6ec56260618b820e19c8bd734fbfb744d refs/conflicts/8b3d16d22d0138ddbe6a1cd1a4cb9abf516a4609 151 | .fi 152 | .if n \{\ 153 | .RE 154 | .\} 155 | .sp 156 | Then Arya fetches and runs \fIgit octopus topic_*\fR from her branch topic_arya\&. It will starts by merging topic_eddard, no conflict happens (assuming), then merges topic_mine\&. The actual conflict of that merge will combine both conflicts because the current state have changes from both topic_arya and topic_eddard\&. This means that this conflict has a new signature that doesn\(cqt have any resolution and the octopus will fail\&. In this case \fIgit octopus\fR is not associative\&. 157 | .SH "GIT\-OCTOPUS" 158 | .sp 159 | Part of the git\-octopus suit\&. 160 | -------------------------------------------------------------------------------- /doc/git-octopus.1: -------------------------------------------------------------------------------- 1 | '\" t 2 | .\" Title: git-octopus 3 | .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] 4 | .\" Generator: DocBook XSL Stylesheets v1.79.1 5 | .\" Date: 11/18/2016 6 | .\" Manual: git-octopus suit Manual 7 | .\" Source: git-octopus 1.4 8 | .\" Language: English 9 | .\" 10 | .TH "GIT\-OCTOPUS" "1" "11/18/2016" "git\-octopus 1\&.4" "git\-octopus suit Manual" 11 | .\" ----------------------------------------------------------------- 12 | .\" * Define some portability stuff 13 | .\" ----------------------------------------------------------------- 14 | .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15 | .\" http://bugs.debian.org/507673 16 | .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html 17 | .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18 | .ie \n(.g .ds Aq \(aq 19 | .el .ds Aq ' 20 | .\" ----------------------------------------------------------------- 21 | .\" * set default formatting 22 | .\" ----------------------------------------------------------------- 23 | .\" disable hyphenation 24 | .nh 25 | .\" disable justification (adjust text to left margin only) 26 | .ad l 27 | .\" ----------------------------------------------------------------- 28 | .\" * MAIN CONTENT STARTS HERE * 29 | .\" ----------------------------------------------------------------- 30 | .SH "NAME" 31 | git-octopus \- extends git\-merge with branch naming patterns\&. 32 | .SH "SYNOPSIS" 33 | .sp 34 | .nf 35 | \fIgit octopus\fR [\-n|\-c] [\-s ] [\-e ] [\&...] 36 | \fIgit octopus\fR \-v 37 | .fi 38 | .SH "DESCRIPTION" 39 | .sp 40 | can be any usual refspec or a naming pattern\&. Patterns are resolved using \fIgit ls\-remote\fR, the resulting refs are given to \fIgit merge\fR\&. 41 | .sp 42 | In case of conflicts, tries to reuse conflict resolutions stored by \fIgit conflict\fR in refs/conflicts/*\&. (See git\-conflict(1))\&. 43 | .sp 44 | In case of a merge failure, iterates over all the refs, computing merges with each ref (from HEAD) one by one for diagnosic purpose\&. Learn more about conflict management on the project\(cqs homepage\&. 45 | .SH "OPTIONS" 46 | .PP 47 | \-n 48 | .RS 4 49 | Leaves the repository back to HEAD\&. 50 | .RE 51 | .PP 52 | \-c 53 | .RS 4 54 | Commit the resulting merge in the current branch\&. This is the default behavior unless 55 | \-n 56 | or 57 | octopus\&.commit 58 | set to false\&. Use this option to override the latter\&. 59 | .RE 60 | .PP 61 | \-s 62 | .RS 4 63 | Chunk mode: the merge is performed by subsets of branches\&. This is meant to help reading the log graph when lots of branches are merged\&. 64 | .RE 65 | .PP 66 | \-e 67 | .RS 4 68 | Exclude pattern: the merge excludes branches matching the \&. 69 | .RE 70 | .PP 71 | \-v 72 | .RS 4 73 | Prints the version of 74 | git\-octopus 75 | .RE 76 | .SH "EXAMPLES" 77 | .sp 78 | .RS 4 79 | .ie n \{\ 80 | \h'-04'\(bu\h'+03'\c 81 | .\} 82 | .el \{\ 83 | .sp -1 84 | .IP \(bu 2.3 85 | .\} 86 | Merge check of the current branch with all feature branches and the master from origin: 87 | .sp 88 | .if n \{\ 89 | .RS 4 90 | .\} 91 | .nf 92 | $ git octopus \-n origin/features/* origin/master 93 | .fi 94 | .if n \{\ 95 | .RE 96 | .\} 97 | .sp 98 | This is basically a merge check you would do before pushing your branch\&. 99 | .RE 100 | .SH "CONFIGURATION" 101 | .PP 102 | octopus\&.commit 103 | .RS 4 104 | true by default\&. set to false behaves like \-n 105 | .RE 106 | .PP 107 | octopus\&.pattern 108 | .RS 4 109 | Defines a branch naming pattern that 110 | \fIgit octopus\fR 111 | would use by default\&. Use multiple lines to define several patterns\&. See 112 | git\-config(1)\&. 113 | .RE 114 | .PP 115 | octopus\&.excludePattern 116 | .RS 4 117 | Defines a branch naming pattern that 118 | \fIgit octopus\fR 119 | will exclude by default\&. 120 | .RE 121 | .SH "SEE ALSO" 122 | .sp 123 | git\-merge(1), git\-ls\-remote(1), git\-conflict(1)\&. 124 | .SH "GIT\-OCTOPUS" 125 | .sp 126 | Part of the git\-octopus suit\&. 127 | -------------------------------------------------------------------------------- /git-octopus.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/lesfurets/git-octopus/git" 5 | "github.com/lesfurets/git-octopus/run" 6 | "log" 7 | "os" 8 | "os/signal" 9 | ) 10 | 11 | func main() { 12 | repo := git.Repository{Path: "."} 13 | 14 | context := run.OctopusContext{ 15 | Repo: &repo, 16 | Logger: log.New(os.Stdout, "", 0), 17 | } 18 | 19 | signalChan := make(chan os.Signal, 1) 20 | signal.Notify(signalChan, os.Interrupt, os.Kill) 21 | 22 | go handleSignals(signalChan, &context) 23 | 24 | err := run.Run(&context, os.Args[1:]...) 25 | 26 | if err != nil { 27 | if len(err.Error()) > 0 { 28 | log.Fatalln(err.Error()) 29 | } 30 | os.Exit(1) 31 | } 32 | } 33 | 34 | func handleSignals(signalChan chan os.Signal, context *run.OctopusContext) { 35 | initialHeadCommit, _ := context.Repo.Git("rev-parse", "HEAD") 36 | /* 37 | The behavior of this is quite tricky. The signal is not only received on signalChan 38 | but sent to subprocesses started by exec.Command as well. It is likely that 39 | the main go routine is running one of those subprocess which will stop and return an error. 40 | The error is handled by the Run function as any other error depending on where the execution was. 41 | 42 | In the mean time, this routine is resetting the repo. 43 | 44 | This is definitly an approximation that works in most cases. 45 | */ 46 | signal := <-signalChan 47 | context.Logger.Printf("Signal %v\n", signal.String()) 48 | context.Repo.Git("reset", "-q", "--hard", initialHeadCommit) 49 | context.Repo.Git("clean", "-fd") 50 | os.Exit(1) 51 | } -------------------------------------------------------------------------------- /git/git.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "errors" 7 | "os/exec" 8 | "strings" 9 | ) 10 | 11 | type LsRemoteEntry struct { 12 | Ref string 13 | Sha1 string 14 | } 15 | 16 | // Takes the output of git-ls-remote. Returns a map refsname => sha1 17 | func ParseLsRemote(lsRemoteOutput string) []LsRemoteEntry { 18 | result := []LsRemoteEntry{} 19 | 20 | if len(lsRemoteOutput) == 0 { 21 | return result 22 | } 23 | 24 | scanner := bufio.NewScanner(strings.NewReader(lsRemoteOutput)) 25 | 26 | for scanner.Scan() { 27 | split := strings.Split(scanner.Text(), "\t") 28 | result = append(result, LsRemoteEntry{Ref: split[1], Sha1: split[0]}) 29 | } 30 | 31 | return result 32 | } 33 | 34 | type Repository struct { 35 | Path string 36 | } 37 | 38 | func (repo *Repository) Git(args ...string) (string, error) { 39 | cmd := exec.Command("git", append([]string{"-C", repo.Path}, args...)...) 40 | errOut := bytes.NewBufferString("") 41 | cmd.Stderr = errOut 42 | out, err := cmd.Output() 43 | 44 | if err != nil { 45 | return "", errors.New(errOut.String()) 46 | } 47 | 48 | stringOut := strings.TrimSpace(string(out[:])) 49 | 50 | return stringOut, err 51 | } 52 | -------------------------------------------------------------------------------- /git/git_test.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "io/ioutil" 6 | "os" 7 | "path/filepath" 8 | "testing" 9 | ) 10 | 11 | func TestParseLsRemoteEmpty(t *testing.T) { 12 | assert.NotNil(t, ParseLsRemote("")) 13 | assert.Equal(t, []LsRemoteEntry{}, ParseLsRemote("")) 14 | } 15 | 16 | func TestParseLsRemote(t *testing.T) { 17 | lsRemoteOutput := "d8dd4eadaf3c1075eff3b7d4fe6bec5fbfe76b4c refs/heads/master\n" + 18 | "5b2b1bf1cdf1150f34bd5809a038b292dc560998 refs/heads/go_rewrite" 19 | expected := []LsRemoteEntry{ 20 | {Ref: "refs/heads/master", Sha1: "d8dd4eadaf3c1075eff3b7d4fe6bec5fbfe76b4c"}, 21 | {Ref: "refs/heads/go_rewrite", Sha1: "5b2b1bf1cdf1150f34bd5809a038b292dc560998"}, 22 | } 23 | assert.Equal(t, expected, ParseLsRemote(lsRemoteOutput)) 24 | } 25 | 26 | func TestGitCommand(t *testing.T) { 27 | dir, _ := ioutil.TempDir("", "git-octopus-test-") 28 | defer os.RemoveAll(dir) 29 | 30 | repo := Repository{Path: dir} 31 | 32 | repo.Git("init") 33 | 34 | _, err := os.Stat(filepath.Join(dir, ".git")) 35 | 36 | assert.Nil(t, err) 37 | } 38 | 39 | func TestGitError(t *testing.T) { 40 | dir, _ := ioutil.TempDir("", "git-octopus-test-") 41 | defer os.RemoveAll(dir) 42 | 43 | repo := Repository{Path: dir} 44 | 45 | repo.Git("init") 46 | 47 | _, err := repo.Git("rev-parse", "HEAD") 48 | 49 | if assert.NotNil(t, err) { 50 | assert.Contains(t, err.Error(), 51 | "ambiguous argument 'HEAD': unknown revision or path not in the working tree.") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /run/basics_test.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "github.com/lesfurets/git-octopus/git" 5 | "github.com/lesfurets/git-octopus/test" 6 | "github.com/stretchr/testify/assert" 7 | "io/ioutil" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | "testing" 12 | ) 13 | 14 | func TestVersion(t *testing.T) { 15 | context, out := CreateTestContext() 16 | defer test.Cleanup(context.Repo) 17 | 18 | Run(context, "-v") 19 | 20 | assert.Equal(t, "2.0\n", out.String()) 21 | } 22 | 23 | func writeFile(repo *git.Repository, name string, lines ...string) { 24 | fileName := filepath.Join(repo.Path, name) 25 | ioutil.WriteFile(fileName, []byte(strings.Join(lines, "\n")), 0644) 26 | } 27 | 28 | // Basic merge of 3 branches. Asserts the resulting tree and the merge commit 29 | func TestOctopus3branches(t *testing.T) { 30 | context, _ := CreateTestContext() 31 | repo := context.Repo 32 | defer test.Cleanup(repo) 33 | 34 | // Create and commit file foo1 in branch1 35 | repo.Git("checkout", "-b", "branch1") 36 | writeFile(repo, "foo1", "First line") 37 | repo.Git("add", "foo1") 38 | repo.Git("commit", "-m\"\"") 39 | 40 | // Create and commit file foo2 in branch2 41 | repo.Git("checkout", "-b", "branch2", "master") 42 | writeFile(repo, "foo2", "First line") 43 | repo.Git("add", "foo2") 44 | repo.Git("commit", "-m\"\"") 45 | 46 | // Create and commit file foo3 in branch3 47 | repo.Git("checkout", "-b", "branch3", "master") 48 | writeFile(repo, "foo3", "First line") 49 | repo.Git("add", "foo3") 50 | repo.Git("commit", "-m\"\"") 51 | 52 | // Merge the 3 branches in a new octopus branch 53 | repo.Git("checkout", "-b", "octopus", "master") 54 | 55 | err := Run(context, "branch*") 56 | assert.Nil(t, err) 57 | 58 | // The working tree should have the 3 files and status should be clean 59 | _, err = os.Open(filepath.Join(repo.Path, "foo1")) 60 | assert.Nil(t, err) 61 | _, err = os.Open(filepath.Join(repo.Path, "foo2")) 62 | assert.Nil(t, err) 63 | _, err = os.Open(filepath.Join(repo.Path, "foo3")) 64 | assert.Nil(t, err) 65 | 66 | status, _ := repo.Git("status", "--porcelain") 67 | assert.Empty(t, status) 68 | 69 | // octopus branch should contain the 3 branches 70 | _, err = repo.Git("merge-base", "--is-ancestor", "branch1", "octopus") 71 | assert.Nil(t, err) 72 | _, err = repo.Git("merge-base", "--is-ancestor", "branch2", "octopus") 73 | assert.Nil(t, err) 74 | _, err = repo.Git("merge-base", "--is-ancestor", "branch3", "octopus") 75 | assert.Nil(t, err) 76 | 77 | // Assert the commit message 78 | commitMessage, _ := repo.Git("show", "--pretty=format:%B") // gets the commit body only 79 | 80 | assert.Contains(t, commitMessage, 81 | "Merged branches:\n"+ 82 | "refs/heads/branch1\n"+ 83 | "refs/heads/branch2\n"+ 84 | "refs/heads/branch3\n"+ 85 | "\nCommit created by git-octopus "+VERSION+".") 86 | } 87 | 88 | func TestOneBranchFastForward(t *testing.T) { 89 | //given 90 | context, _ := CreateTestContext() 91 | repo := context.Repo 92 | defer test.Cleanup(repo) 93 | 94 | repo.Git("checkout", "-b", "feature/test") 95 | writeFile(repo, "testFeature", "") 96 | repo.Git("add", "testFeature") 97 | repo.Git("commit", "-m", "add testFeature") 98 | testFeatureSha1, _ := repo.Git("rev-parse", "HEAD") 99 | repo.Git("checkout", "master") 100 | 101 | //when 102 | Run(context, "feature/*") 103 | 104 | //then 105 | // feature/test should be merged in master 106 | actual, _ := repo.Git("branch", "--contains", "feature/test") 107 | assert.Contains(t, actual, "master") 108 | 109 | // Status should be clean 110 | status, _ := context.Repo.Git("status", "--porcelain") 111 | assert.Empty(t, status) 112 | 113 | // Assert that master has been fast forwarded to feature/test 114 | masterSha1, _ := repo.Git("rev-parse", "HEAD") 115 | assert.Equal(t, testFeatureSha1, masterSha1) 116 | 117 | } 118 | 119 | func TestTwoBranchesFastForward(t *testing.T) { 120 | //given 121 | context, _ := CreateTestContext() 122 | repo := context.Repo 123 | defer test.Cleanup(repo) 124 | 125 | //a feature branch from master with a commit 126 | repo.Git("checkout", "-b", "feature/test") 127 | writeFile(repo, "testFeature", "firstline") 128 | repo.Git("add", "testFeature") 129 | repo.Git("commit", "-m", "add testFeature") 130 | 131 | //a second branch from feature/test with a commit 132 | repo.Git("checkout", "-b", "feature/test2") 133 | writeFile(repo, "testFeature", "firstline", "secondline") 134 | repo.Git("add", "testFeature") 135 | repo.Git("commit", "-m", "modify testFeature") 136 | 137 | testFeatureSha1, _ := repo.Git("rev-parse", "HEAD") 138 | repo.Git("checkout", "master") 139 | 140 | //when 141 | Run(context, "feature/*") 142 | 143 | //then 144 | // feature/test2 should be merged in master 145 | actual, _ := repo.Git("branch", "--contains", "feature/test2") 146 | assert.Contains(t, actual, "master") 147 | 148 | // Status should be clean 149 | status, _ := context.Repo.Git("status", "--porcelain") 150 | assert.Empty(t, status) 151 | 152 | // Assert that master has been fast forwarded to feature/test2 153 | masterSha1, _ := repo.Git("rev-parse", "HEAD") 154 | assert.Equal(t, testFeatureSha1, masterSha1) 155 | } 156 | -------------------------------------------------------------------------------- /run/context_test.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/lesfurets/git-octopus/git" 7 | "github.com/lesfurets/git-octopus/test" 8 | "log" 9 | ) 10 | 11 | func CreateTestContext() (*OctopusContext, *bytes.Buffer) { 12 | dir := test.CreateTempDir() 13 | 14 | repo := git.Repository{Path: dir} 15 | 16 | repo.Git("init") 17 | repo.Git("config", "user.name", "gotest") 18 | repo.Git("config", "user.email", "gotest@golang.com") 19 | _, err := repo.Git("commit", "--allow-empty", "-m\"first commit\"") 20 | 21 | if err != nil { 22 | fmt.Println("There's something wrong with the git installation:") 23 | fmt.Println(err.Error()) 24 | } 25 | 26 | out := bytes.NewBufferString("") 27 | 28 | context := OctopusContext{ 29 | Repo: &repo, 30 | Logger: log.New(out, "", 0), 31 | } 32 | 33 | return &context, out 34 | } 35 | -------------------------------------------------------------------------------- /run/edge_cases_test.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "github.com/lesfurets/git-octopus/test" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestOctopusCommitConfigError(t *testing.T) { 10 | context, _ := CreateTestContext() 11 | defer test.Cleanup(context.Repo) 12 | 13 | context.Repo.Git("config", "octopus.commit", "bad_value") 14 | 15 | err := Run(context, "-v") 16 | 17 | assert.NotNil(t, err) 18 | } 19 | 20 | func TestOctopusNoPatternGiven(t *testing.T) { 21 | context, out := CreateTestContext() 22 | defer test.Cleanup(context.Repo) 23 | 24 | Run(context) 25 | 26 | assert.Equal(t, "Nothing to merge. No pattern given\n", out.String()) 27 | } 28 | 29 | func TestOctopusNoBranchMatching(t *testing.T) { 30 | context, out := CreateTestContext() 31 | defer test.Cleanup(context.Repo) 32 | 33 | Run(context, "refs/remotes/dumb/*", "refs/remotes/dumber/*") 34 | 35 | assert.Contains(t, out.String(), "No branch matching \"refs/remotes/dumb/* refs/remotes/dumber/*\" were found\n") 36 | } 37 | 38 | // Merge a branch that is already merged. 39 | // Should be noop and print something accordingly 40 | func TestOctopusAlreadyUpToDate(t *testing.T) { 41 | context, out := CreateTestContext() 42 | defer test.Cleanup(context.Repo) 43 | 44 | // commit a file in master 45 | writeFile(context.Repo, "foo", "First line") 46 | context.Repo.Git("add", "foo") 47 | context.Repo.Git("commit", "-m\"first commit\"") 48 | 49 | // Create a branch on this first commit. 50 | // master and outdated_branch are on the same commit 51 | context.Repo.Git("branch", "outdated_branch") 52 | 53 | expected, _ := context.Repo.Git("rev-parse", "HEAD") 54 | 55 | err := Run(context, "outdated_branch") 56 | 57 | actual, _ := context.Repo.Git("rev-parse", "HEAD") 58 | 59 | // HEAD should point to the same commit 60 | assert.Equal(t, expected, actual) 61 | 62 | // This is a normal behavious, no error should be raised 63 | assert.Nil(t, err) 64 | 65 | assert.Contains(t, out.String(), "Already up-to-date with refs/heads/outdated_branch") 66 | } 67 | 68 | // git-octopus should prevent from running if status is not clean 69 | func TestUncleanStateFail(t *testing.T) { 70 | context, _ := CreateTestContext() 71 | defer test.Cleanup(context.Repo) 72 | 73 | // create and commit a file 74 | writeFile(context.Repo, "foo", "First line") 75 | 76 | err := Run(context, "*") 77 | 78 | if assert.NotNil(t, err) { 79 | assert.Contains(t, err.Error(), "The repository has to be clean.") 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /run/matcher.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/lesfurets/git-octopus/git" 7 | "log" 8 | "strings" 9 | ) 10 | 11 | func resolveBranchList(repo *git.Repository, logger *log.Logger, patterns []string, excludedPatterns []string) []git.LsRemoteEntry { 12 | lsRemote, _ := repo.Git(append([]string{"ls-remote", "."}, patterns...)...) 13 | includedRefs := git.ParseLsRemote(lsRemote) 14 | excludedRefs := []git.LsRemoteEntry{} 15 | 16 | totalCount := len(includedRefs) 17 | excludedCount := 0 18 | 19 | if len(excludedPatterns) > 0 { 20 | lsRemote, _ = repo.Git(append([]string{"ls-remote", "."}, excludedPatterns...)...) 21 | excludedRefs = git.ParseLsRemote(lsRemote) 22 | } 23 | 24 | tempBuffer := bytes.NewBufferString("") 25 | 26 | if totalCount == 0 { 27 | tempBuffer.WriteString(fmt.Sprintf("No branch matching \"%v\" were found\n", strings.Join(patterns, " "))) 28 | } 29 | 30 | result := []git.LsRemoteEntry{} 31 | 32 | for _, lsRemoteEntry := range includedRefs { 33 | excluded := false 34 | for _, excl := range excludedRefs { 35 | if excl.Ref == lsRemoteEntry.Ref { 36 | excludedCount++ 37 | excluded = true 38 | break 39 | } 40 | } 41 | 42 | if excluded { 43 | tempBuffer.WriteString("E ") 44 | } else { 45 | tempBuffer.WriteString("I ") 46 | result = append(result, lsRemoteEntry) 47 | } 48 | tempBuffer.WriteString(lsRemoteEntry.Ref + "\n") 49 | } 50 | 51 | count := len(result) 52 | 53 | logger.Printf("%v branches (I)ncluded (%v matching, %v (E)xcluded):\n", count, totalCount, excludedCount) 54 | logger.Print(tempBuffer.String()) 55 | 56 | return result 57 | } 58 | -------------------------------------------------------------------------------- /run/matcher_test.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "bytes" 5 | "github.com/lesfurets/git-octopus/git" 6 | "github.com/lesfurets/git-octopus/test" 7 | "github.com/stretchr/testify/assert" 8 | "testing" 9 | ) 10 | 11 | func setupRepo() (*OctopusContext, *bytes.Buffer) { 12 | context, out := CreateTestContext() 13 | head, _ := context.Repo.Git("rev-parse", "HEAD") 14 | context.Repo.Git("update-ref", "refs/heads/test1", head) 15 | context.Repo.Git("update-ref", "refs/remotes/origin/test1", head) 16 | context.Repo.Git("update-ref", "refs/remotes/origin/test2", head) 17 | context.Repo.Git("update-ref", "refs/remotes/origin/test3", head) 18 | 19 | return context, out 20 | } 21 | 22 | func TestResolveBranchListSimple(t *testing.T) { 23 | context, out := setupRepo() 24 | defer test.Cleanup(context.Repo) 25 | 26 | head, _ := context.Repo.Git("rev-parse", "HEAD") 27 | 28 | branchList := resolveBranchList(context.Repo, context.Logger, []string{"refs/heads/*"}, nil) 29 | 30 | expected := []git.LsRemoteEntry{ 31 | {Ref: "refs/heads/master", Sha1: head}, 32 | {Ref: "refs/heads/test1", Sha1: head}, 33 | } 34 | 35 | assert.Equal(t, expected, branchList) 36 | outputString := out.String() 37 | assert.Contains(t, outputString, 38 | "2 branches (I)ncluded (2 matching, 0 (E)xcluded):\n"+ 39 | "I refs/heads/master\n"+ 40 | "I refs/heads/test1\n") 41 | } 42 | 43 | func TestResolveBranchListExclusion(t *testing.T) { 44 | context, out := setupRepo() 45 | defer test.Cleanup(context.Repo) 46 | 47 | head, _ := context.Repo.Git("rev-parse", "HEAD") 48 | 49 | branchList := resolveBranchList(context.Repo, context.Logger, []string{"refs/heads/*", "remotes/origin/*"}, []string{"*/test1"}) 50 | 51 | expected := []git.LsRemoteEntry{ 52 | {Ref: "refs/heads/master", Sha1: head}, 53 | {Ref: "refs/remotes/origin/test2", Sha1: head}, 54 | {Ref: "refs/remotes/origin/test3", Sha1: head}, 55 | } 56 | 57 | assert.Equal(t, expected, branchList) 58 | 59 | outputString := out.String() 60 | assert.Contains(t, outputString, 61 | "3 branches (I)ncluded (5 matching, 2 (E)xcluded):\n"+ 62 | "I refs/heads/master\n"+ 63 | "E refs/heads/test1\n"+ 64 | "E refs/remotes/origin/test1\n"+ 65 | "I refs/remotes/origin/test2\n"+ 66 | "I refs/remotes/origin/test3\n") 67 | } 68 | -------------------------------------------------------------------------------- /run/no_commit_test.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "github.com/lesfurets/git-octopus/test" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestFastForward(t *testing.T) { 10 | context, _ := CreateTestContext() 11 | repo := context.Repo 12 | defer test.Cleanup(repo) 13 | 14 | // The repo is on master branch with an empty tree 15 | // Create a branch with a new file 16 | repo.Git("checkout", "-b", "new_branch") 17 | writeFile(repo, "foo", "bar") 18 | repo.Git("add", "foo") 19 | repo.Git("commit", "-m", "added foo") 20 | 21 | repo.Git("checkout", "master") 22 | 23 | expected, _ := repo.Git("rev-parse", "HEAD") 24 | 25 | Run(context, "-n", "new_branch") 26 | 27 | actual, _ := repo.Git("rev-parse", "HEAD") 28 | assert.Equal(t, expected, actual) 29 | 30 | status, _ := repo.Git("status", "--porcelain") 31 | assert.Empty(t, status) 32 | } 33 | 34 | func TestConflictState(t *testing.T) { 35 | context, _ := CreateTestContext() 36 | repo := context.Repo 37 | defer test.Cleanup(repo) 38 | 39 | writeFile(repo, "foo", "line 1", "") 40 | repo.Git("add", ".") 41 | repo.Git("commit", "-m", "added foo") 42 | 43 | writeFile(repo, "foo", "line 1", "line 2") 44 | repo.Git("commit", "-a", "-m", "edited foo") 45 | 46 | repo.Git("checkout", "-b", "a_branch", "HEAD^") 47 | 48 | writeFile(repo, "foo", "line 1", "line 2 bis") 49 | repo.Git("commit", "-a", "-m", "edited foo in parallel to master") 50 | 51 | repo.Git("checkout", "master") 52 | expected, _ := repo.Git("rev-parse", "HEAD") 53 | 54 | err := Run(context, "-n", "a_branch") 55 | 56 | assert.NotNil(t, err) 57 | actual, _ := repo.Git("rev-parse", "HEAD") 58 | assert.Equal(t, expected, actual) 59 | 60 | status, _ := repo.Git("status", "--porcelain") 61 | assert.Empty(t, status) 62 | } 63 | -------------------------------------------------------------------------------- /run/run.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "github.com/lesfurets/git-octopus/config" 7 | "github.com/lesfurets/git-octopus/git" 8 | "log" 9 | ) 10 | 11 | type OctopusContext struct { 12 | Repo *git.Repository 13 | Logger *log.Logger 14 | } 15 | 16 | const VERSION = "2.0" 17 | 18 | func Run(context *OctopusContext, args ...string) error { 19 | 20 | octopusConfig, err := config.GetOctopusConfig(context.Repo, args) 21 | 22 | if err != nil { 23 | return err 24 | } 25 | 26 | if octopusConfig.PrintVersion { 27 | context.Logger.Println(VERSION) 28 | return nil 29 | } 30 | 31 | if len(octopusConfig.Patterns) == 0 { 32 | context.Logger.Println("Nothing to merge. No pattern given") 33 | return nil 34 | } 35 | 36 | status, _ := context.Repo.Git("status", "--porcelain") 37 | 38 | // This is not formally required but it would be an ambiguous behaviour to let git-octopus run on unclean state. 39 | if len(status) != 0 { 40 | return errors.New("The repository has to be clean.") 41 | } 42 | 43 | branchList := resolveBranchList(context.Repo, context.Logger, octopusConfig.Patterns, octopusConfig.ExcludedPatterns) 44 | 45 | if len(branchList) == 0 { 46 | return nil 47 | } 48 | 49 | initialHeadCommit, _ := context.Repo.Git("rev-parse", "HEAD") 50 | 51 | if !octopusConfig.DoCommit { 52 | defer context.Repo.Git("reset", "-q", "--hard", initialHeadCommit) 53 | } 54 | 55 | context.Logger.Println() 56 | 57 | parents, err := mergeHeads(context, branchList) 58 | 59 | if err != nil { 60 | return err 61 | } 62 | 63 | if !octopusConfig.DoCommit { 64 | return nil 65 | } 66 | 67 | if len(parents) == 1 { 68 | // This is either a fast-forward update or a no op 69 | context.Repo.Git("update-ref", "HEAD", parents[0]) 70 | } else { 71 | // We need at least 2 parents to create a merge commit 72 | tree, _ := context.Repo.Git("write-tree") 73 | args := []string{"commit-tree"} 74 | for _, parent := range parents { 75 | args = append(args, "-p", parent) 76 | } 77 | args = append(args, "-m", octopusCommitMessage(branchList), tree) 78 | commit, _ := context.Repo.Git(args...) 79 | context.Repo.Git("update-ref", "HEAD", commit) 80 | } 81 | 82 | return nil 83 | } 84 | 85 | // The logic of this function is copied directly from git-merge-octopus.sh 86 | func mergeHeads(context *OctopusContext, remotes []git.LsRemoteEntry) ([]string, error) { 87 | head, _ := context.Repo.Git("rev-parse", "--verify", "-q", "HEAD") 88 | 89 | // The list of commits that has already merged in the current tree. Originally called 'MRC' in git-merge-octopus.sh 90 | mergedHeads := []string{head} 91 | // currentTree originally called 'MRT' in git-merge-octopus.sh 92 | currentTree, _ := context.Repo.Git("write-tree") 93 | isFfMerge := true 94 | 95 | for _, lsRemoteEntry := range remotes { 96 | common, err := context.Repo.Git(append([]string{"merge-base", "--all", lsRemoteEntry.Sha1}, mergedHeads...)...) 97 | 98 | if err != nil { 99 | return nil, errors.New("Unable to find common commit with " + lsRemoteEntry.Ref) 100 | } 101 | 102 | if common == lsRemoteEntry.Sha1 { 103 | context.Logger.Println("Already up-to-date with " + lsRemoteEntry.Ref) 104 | continue 105 | } 106 | 107 | if len(mergedHeads) == 1 && common == mergedHeads[0] && isFfMerge { 108 | context.Logger.Println("Fast-forwarding to: " + lsRemoteEntry.Ref) 109 | 110 | _, err := context.Repo.Git("read-tree", "-u", "-m", mergedHeads[0], lsRemoteEntry.Sha1) 111 | 112 | if err != nil { 113 | return nil, err 114 | } 115 | 116 | mergedHeads[0] = lsRemoteEntry.Sha1 117 | currentTree, _ = context.Repo.Git("write-tree") 118 | continue 119 | } 120 | 121 | isFfMerge = false 122 | 123 | context.Logger.Println("Trying simple merge with " + lsRemoteEntry.Ref) 124 | 125 | _, err = context.Repo.Git("read-tree", "-u", "-m", "--aggressive", common, currentTree, lsRemoteEntry.Sha1) 126 | 127 | if err != nil { 128 | return nil, err 129 | } 130 | 131 | nextTree, err := context.Repo.Git("write-tree") 132 | 133 | if err != nil { 134 | context.Logger.Println("Simple merge did not work, trying automatic merge.") 135 | _, err = context.Repo.Git("merge-index", "-o", "git-merge-one-file", "-a") 136 | 137 | if err != nil { 138 | context.Logger.Println("Automated merge did not work.") 139 | context.Logger.Println("Should not be doing an Octopus.") 140 | return nil, errors.New("") 141 | } 142 | 143 | nextTree, _ = context.Repo.Git("write-tree") 144 | } 145 | 146 | mergedHeads = append(mergedHeads, lsRemoteEntry.Sha1) 147 | currentTree = nextTree 148 | } 149 | 150 | return mergedHeads, nil 151 | } 152 | 153 | func octopusCommitMessage(remotes []git.LsRemoteEntry) string { 154 | buf := bytes.NewBufferString("Merged branches:\n") 155 | for _, lsRemoteEntry := range remotes { 156 | buf.WriteString(lsRemoteEntry.Ref + "\n") 157 | } 158 | buf.WriteString("\nCommit created by git-octopus " + VERSION + ".\n") 159 | return buf.String() 160 | } 161 | -------------------------------------------------------------------------------- /src/doc/git-conflict.1.txt: -------------------------------------------------------------------------------- 1 | :man manual: git-octopus suit Manual 2 | :man source: git-octopus 3 | :man version: 1.4 4 | 5 | git-conflict(1) 6 | ============== 7 | 8 | NAME 9 | ---- 10 | git-conflict - record conflict resolution. 11 | 12 | SYNOPSIS 13 | -------- 14 | [verse] 15 | 'git conflict' 16 | 'git conflict' --continue | --abort | -l 17 | 18 | DESCRIPTION 19 | ----------- 20 | 21 | 'git conflict' records a conflict resolution between two branches that 'git octopus' will be able to reuse. 22 | 23 | Start the recording process between the current branch and an other branch with 24 | 25 | ------------------------------------------------------------------------------------ 26 | $ git conflict an_other_branch 27 | ------------------------------------------------------------------------------------ 28 | 29 | This will set the index and the working tree in the conflict state. 30 | Once you've resolved it using 'git add', run 'git conflict --continue' to record the resolution. 31 | It will print a ref that points to the resolution and resets the repository back to HEAD. 32 | 33 | ------------------------------------------------------------------------------------ 34 | $ git conflict --continue 35 | Conflict resolution created: refs/conflicts/75dd2c142264373c96a3341b2f9fb57bec7a7b7d 36 | ------------------------------------------------------------------------------------ 37 | 38 | So 'git conflict' doesn't write history in your current branch. 39 | 40 | 'git conflict --continue' fails if a resolution for the same conflict already exists. To override a resolution you will need to delete the ref with 'git update-ref -d ' before recording the new one and possibly force the push. 41 | 42 | If you're satisfied with the resolution you can push it using a simple 'git push'. 43 | 44 | ------------------------------------------------------------------------------------ 45 | $ git push origin refs/conflicts/75dd2c142264373c96a3341b2f9fb57bec7a7b7d 46 | ------------------------------------------------------------------------------------ 47 | 48 | OPTIONS 49 | ------- 50 | -l:: 51 | + 52 | List all resolutions 53 | 54 | --abort:: 55 | + 56 | Abort the current conflict resolution process, and try to reconstruct the pre-merge state. 57 | + 58 | If there were uncommitted worktree changes present when the merge started, git conflict --abort will in some cases be unable to reconstruct these changes. It is therefore recommended to always commit or stash your changes before running git conflict. 59 | 60 | --continue:: 61 | + 62 | Record the index state as the resolution of the conflict and prints its ref. The index and the working tree are reset back to HEAD. 63 | 64 | INTERNALS 65 | --------- 66 | The first thing to know is that a conflict ref, for instance `refs/conflicts/75dd2c142264373c96a3341b2f9fb57bec7a7b7d`, is a standard commit. You can inspect it with 'git show' or any logger tool. It has an author and dates etc... 67 | 68 | The sha1 in the ref is a hash of the content of the conflict that has been resolved. This allows resolutions to be found easily by other commands given a conflict state by simply hashing the conflict content and looking for a `refs/conflict/` of the same name. A resolution is not related to a particular branch, it can be applied in any circumstances that produced the same conflict. This means that you can continue to commit on your branches, you can rebase them, rename them anyhow. As long as the same conflict is produced, the resolution can be retrieved. 69 | 70 | LIMITATIONS 71 | ----------- 72 | Currently, when reapplying a resolution, it will only look at `both modified` files. So conflicts with renames, mod changes and deletions are not handled. In addition, If you changed something in a file that wasn't in conflict state, it won't be reapply neither. 73 | 74 | Managing conflicts with more than a single branch is subtle. Let's say I'm on a local branch `topic_mine` and I have two different conflicts with `topic_arya` and `topic_eddard` 75 | ------------------------- 76 | A---B---C topic_mine 77 | / 78 | D---E---F---G topic_eddard 79 | \ 80 | H---I topic_arya 81 | ------------------------- 82 | I can write two resolutions 83 | ------------------------------------------------------------------------------------ 84 | $ git conflict topic_arya 85 | ... 86 | $ git conflict --continue 87 | Conflict resolution created: refs/conflicts/9a4b40f6ec56260618b820e19c8bd734fbfb744d 88 | $ git conflict topic_eddard 89 | ... 90 | git conflict --continue 91 | Conflict resolution created: refs/conflicts/8b3d16d22d0138ddbe6a1cd1a4cb9abf516a4609 92 | ------------------------------------------------------------------------------------ 93 | If I run 'git octopus topic_*' on my current branch (`topic_mine`), it will starts by merging `topic_arya` (branches are sorted alphabeticaly), will find the first resolution and then apply changes from `topic_eddard`, find the 2nd resolution and end up with a success. Let's push ! 94 | --------------------------------------------------------------------------------------------------------------------------------- 95 | $ git push origin refs/conflicts/9a4b40f6ec56260618b820e19c8bd734fbfb744d refs/conflicts/8b3d16d22d0138ddbe6a1cd1a4cb9abf516a4609 96 | --------------------------------------------------------------------------------------------------------------------------------- 97 | Then Arya fetches and runs 'git octopus topic_*' from her branch `topic_arya`. It will starts by merging `topic_eddard`, no conflict happens (assuming), then merges `topic_mine`. The actual conflict of that merge will combine both conflicts because the current state have changes from both `topic_arya` and `topic_eddard`. This means that this conflict has a new signature that doesn't have any resolution and the octopus will fail. 98 | In this case 'git octopus' is not associative. 99 | 100 | GIT-OCTOPUS 101 | ----------- 102 | Part of the https://github.com/lesfurets/git-octopus[git-octopus] suit. -------------------------------------------------------------------------------- /src/doc/git-octopus.1.txt: -------------------------------------------------------------------------------- 1 | :man manual: git-octopus suit Manual 2 | :man source: git-octopus 3 | :man version: 1.4 4 | 5 | git-octopus(1) 6 | ============== 7 | 8 | NAME 9 | ---- 10 | git-octopus - extends git-merge with branch naming patterns. 11 | 12 | SYNOPSIS 13 | -------- 14 | [verse] 15 | 'git octopus' [-n|-c] [-s ] [-e ] [...] 16 | 'git octopus' -v 17 | 18 | DESCRIPTION 19 | ----------- 20 | can be any usual refspec or a naming pattern. 21 | Patterns are resolved using 'git ls-remote', the resulting refs are given to 'git merge'. 22 | 23 | In case of conflicts, tries to reuse conflict resolutions stored by 'git conflict' in `refs/conflicts/*`. (See link:git-conflict.html[git-conflict(1)]). 24 | 25 | In case of a merge failure, iterates over all the refs, computing merges with each ref (from HEAD) one by one for diagnosic purpose. 26 | Learn more about conflict management on the project's https://github.com/lesfurets/git-octopus[homepage]. 27 | 28 | OPTIONS 29 | ------- 30 | -n:: 31 | + 32 | Leaves the repository back to HEAD. 33 | 34 | -c:: 35 | + 36 | Commit the resulting merge in the current branch. This is the default behavior unless `-n` or `octopus.commit` set to false. Use this option to override the latter. 37 | 38 | -s :: 39 | + 40 | Chunk mode: the merge is performed by subsets of branches. This is meant to help reading the log graph when lots of branches are merged. 41 | 42 | -e :: 43 | + 44 | Exclude pattern: the merge excludes branches matching the . 45 | 46 | -v:: 47 | + 48 | Prints the version of `git-octopus` 49 | 50 | EXAMPLES 51 | -------- 52 | * Merge check of the current branch with all feature branches and the master from origin: 53 | + 54 | --------------------------------------------------- 55 | $ git octopus -n origin/features/* origin/master 56 | --------------------------------------------------- 57 | + 58 | This is basically a merge check you would do before pushing your branch. 59 | 60 | CONFIGURATION 61 | ------------- 62 | octopus.commit:: 63 | + 64 | true by default. set to false behaves like -n 65 | 66 | octopus.pattern:: 67 | + 68 | Defines a branch naming pattern that 'git octopus' would use by default. Use multiple lines to define several patterns. See link:git-config.html[git-config(1)]. 69 | 70 | octopus.excludePattern:: 71 | + 72 | Defines a branch naming pattern that 'git octopus' will exclude by default. 73 | 74 | SEE ALSO 75 | -------- 76 | link:git-merge.html[git-merge(1)], link:git-ls-remote.html[git-ls-remote(1)], link:git-conflict.html[git-conflict(1)]. 77 | 78 | GIT-OCTOPUS 79 | ----------- 80 | Part of the https://github.com/lesfurets/git-octopus[git-octopus] suit. -------------------------------------------------------------------------------- /src/git-apply-conflict-resolution: -------------------------------------------------------------------------------- 1 | #Resolve conflicts marked in the index. Files in conflict must have been merged already with git-merge-index 2 | usage() { 3 | cat < 6 | EOF 7 | exit 8 | } 9 | 10 | [ "$1" == "-h" ] && usage 11 | 12 | conflictName= 13 | 14 | if [ "$1" ] ; then 15 | git show-ref -q --verify "refs/conflicts/$1" || die "Ref not found: refs/conflicts/$1" 16 | conflictName=$1 17 | else 18 | #Reading from the index 19 | conflictName=$(hash-conflict) 20 | 21 | git show-ref -q --verify "refs/conflicts/$conflictName" || exit 1 22 | fi 23 | 24 | echo "Applying conflict resolution $conflictName" 25 | 26 | resolutionCommit=$(git rev-parse "refs/conflicts/$conflictName") 27 | leftParent=$(git rev-parse ${resolutionCommit}^1) 28 | rightParent=$(git rev-parse ${resolutionCommit}^2) 29 | commonAncestor=$(git merge-base $leftParent $rightParent) 30 | 31 | cdup="$(git rev-parse --show-cdup)" 32 | 33 | SUCCESS=true 34 | 35 | while read status ; do 36 | state=$(echo $status | awk '{print $1;}') 37 | path=$(echo $status | awk '{print $2;}') 38 | path="$cdup$path" 39 | case $state in 40 | "UU") 41 | # Clean conflict markers 42 | perl -pi -e "s/<<<<<<<.*/<<<<<<< /g" $path 43 | perl -pi -e "s/>>>>>>>.*/>>>>>>> /g" $path 44 | 45 | # Store the current preimage 46 | current_preimage="$cdup$(git unpack-file $(git hash-object -w $path))" 47 | 48 | # Compute the preimage as it was when the resolution was recorded 49 | leftFile="$cdup$(git unpack-file $(git ls-tree $leftParent $path | awk '{print $3;}'))" 50 | rightFile="$cdup$(git unpack-file $(git ls-tree $rightParent $path | awk '{print $3;}'))" 51 | ancestorFile="$cdup$(git unpack-file $(git ls-tree $commonAncestor $path | awk '{print $3;}'))" 52 | 53 | # TODO mettre dans un tmp 54 | recorded_preimage="$cdup$(git unpack-file $(git merge-file -L "" -L "" -L "" --stdout $leftFile $ancestorFile $rightFile | git hash-object --stdin -w))" 55 | 56 | if [ "$(hash-conflict -o -i $current_preimage)" != "$(hash-conflict -o -i $recorded_preimage)" ] ; then 57 | rm $recorded_preimage 58 | recorded_preimage="$cdup$(git unpack-file $(git merge-file -L "" -L "" -L "" --stdout $rightFile $ancestorFile $leftFile | git hash-object --stdin -w))" 59 | fi 60 | 61 | # Do the final merge between the initial conflict, the current conflict and the resolution 62 | # This is based on what is done in rerere.c 63 | resolutonFile="$cdup$(git unpack-file $(git ls-tree $resolutionCommit $path | awk '{print $3;}'))" 64 | 65 | git merge-file --stdout $current_preimage $recorded_preimage $resolutonFile > $path \ 66 | && git update-index --add $path \ 67 | || SUCCESS=false 68 | 69 | rm $current_preimage $leftFile $rightFile $ancestorFile $recorded_preimage $resolutonFile 70 | 71 | ;; 72 | esac 73 | done <<< "$(git status --porcelain | grep "^U.\|^.U\|^AA\|^DD")" 74 | 75 | if ! $SUCCESS ; then die "Conflict not resolved" ; fi 76 | -------------------------------------------------------------------------------- /src/git-conflict: -------------------------------------------------------------------------------- 1 | usage() { 2 | cat < 4 | or: git conflict --continue | --abort 5 | or: git conflict -l 6 | EOF 7 | exit 8 | } 9 | 10 | gitDir=$(git rev-parse --git-dir) 11 | conflictName="${gitDir}/CONFLICT_NAME" 12 | mergeHead="${gitDir}/MERGE_HEAD" 13 | 14 | cleanup () { 15 | rm $conflictName $mergeHead &> /dev/null 16 | } 17 | 18 | case "$1" in 19 | --continue) 20 | [ ! -f $conflictName ] && die "There is no resolution ongoing." 21 | 22 | [ -z "$(git ls-files --unmerged)" ] || die "All conflicts must be resolved" 23 | # if all conflicts are resolved ... 24 | tree=$(git write-tree) 25 | commit=$(git commit-tree $tree -m "resolution between $(git symbolic-ref -q HEAD || git rev-parse HEAD) and $(cat $mergeHead)" -p HEAD -p $(cat $mergeHead)) 26 | conflictRef="refs/conflicts/$(cat $conflictName)" 27 | git update-ref "$conflictRef" "$commit" "" &> /dev/null || 28 | die "Resolution $conflictRef already exists. "\ 29 | "If you want to override it, delete it first :${LF}git update-ref -d $conflictRef" 30 | echo "Conflict resolution created: $conflictRef" 31 | cleanup 32 | git reset -q --hard 33 | ;; 34 | --abort) 35 | git reset --merge 36 | cleanup 37 | exit 0 38 | ;; 39 | -*) 40 | while getopts "lh" opt; do 41 | case "$opt" in 42 | l) 43 | git ls-remote . refs/conflicts/* 44 | ;; 45 | h) 46 | usage 47 | ;; 48 | \?) 49 | exit 1 50 | ;; 51 | esac 52 | done 53 | ;; 54 | *) 55 | remote=$1 56 | git show-ref -q "$remote" || die "Ref not found : $remote" 57 | 58 | [ -f $conflictName ] && die "You are in the middle of a resolution" 59 | 60 | [[ -n $(git diff-index HEAD) ]] && die "The repository has to be clean" 61 | 62 | echo $(git rev-parse $remote) > $mergeHead 63 | 64 | common=$(git merge-base --all $remote HEAD) 65 | 66 | git read-tree -u -m --aggressive $common HEAD $remote 67 | 68 | git-merge-index -o git-merge-one-file -a 2> /dev/null 69 | 70 | if test $? -eq 0 ; then 71 | git reset -q --hard 72 | echo "No conflict with $remote" 73 | cleanup 74 | exit 0 75 | fi 76 | 77 | hash-conflict > $conflictName 78 | ;; 79 | esac -------------------------------------------------------------------------------- /src/git-octopus: -------------------------------------------------------------------------------- 1 | usage() { 2 | cat <...] 4 | 5 | -n leaves the repository back to HEAD 6 | -c Commit the resulting merge in the current branch. 7 | -s do the octopus by chunk of n branches. 8 | -e

exclude branches matching the pattern. 9 | -v prints the version of git-octopus 10 | EOF 11 | exit 12 | } 13 | 14 | removeAllFrom() { 15 | from=$1 16 | remove=$2 17 | for i in $remove; do 18 | from=${from/$i/} 19 | done 20 | echo "$from" 21 | } 22 | 23 | line_break() { 24 | echo "-----------------------------------------------------------" 25 | } 26 | 27 | # Save the current state of the repository in $triggeredBranch 28 | triggeredBranch=$(git symbolic-ref --short HEAD 2> /dev/null) || 29 | triggeredBranch=$(git rev-parse HEAD) 30 | # We save the current HEAD in case of an octopus by chunk 31 | triggeredSha1=$(git rev-parse --verify HEAD) 32 | 33 | 34 | signalHandler() { 35 | echo 36 | line_break 37 | echo "Stoping..." 38 | echo "HEAD -> $triggeredBranch" 39 | git reset -q --hard $triggeredSha1 40 | git checkout -q $triggeredBranch 41 | git clean -d -f 42 | exit 1 43 | } 44 | 45 | doCommit=$(git config octopus.commit) 46 | splitByChunk=false 47 | while getopts "nhvcs:e:" opt; do 48 | case "$opt" in 49 | h) 50 | usage 51 | ;; 52 | n) 53 | doCommit=false 54 | ;; 55 | c) 56 | doCommit=true 57 | ;; 58 | v) 59 | echo "1.4" 60 | exit 0 61 | ;; 62 | s) 63 | [[ $OPTARG =~ ^-?[0-9]+$ ]] || die "-s argument must be a postive number" 64 | splitByChunk=true 65 | chunkSize=$OPTARG 66 | ;; 67 | e) 68 | exclude+=" $OPTARG" 69 | ;; 70 | \?) 71 | exit 1 72 | ;; 73 | esac 74 | done 75 | 76 | [[ -z $(git status --porcelain) ]] || die "The repository has to be clean" 77 | 78 | trap signalHandler SIGINT SIGQUIT 79 | 80 | #Shift all options in order to iterate over refspec-patterns 81 | shift $(expr $OPTIND - 1) 82 | 83 | #Retrive patterns written in the conf 84 | patterns=$(git config --get-all octopus.pattern) 85 | excludePatterns=$(git config --get-all octopus.excludePattern) 86 | 87 | #Overriding the conf with the patterns given as parameters 88 | if [[ -n "$@" ]] ; then 89 | patterns=$@ 90 | fi 91 | if [[ -n "$exclude" ]]; then 92 | excludePatterns=${exclude:1} 93 | fi 94 | 95 | #Exit code 0 if nothing to merge 96 | if [[ -z "$patterns" ]] ; then 97 | exit 0 98 | fi 99 | 100 | branches=$(git ls-remote . $patterns | cut -d $'\t' -f 2) 101 | #Get nothing if excludePatterns is empty 102 | [[ -n "$excludePatterns" ]] && excludedBranches=$(git ls-remote . $excludePatterns | cut -d $'\t' -f 2) 103 | branches=$(removeAllFrom "$branches" "$excludedBranches") 104 | 105 | [[ -z "$excludedBranches" ]] || echo "Excluding branches :" 106 | for branch in $excludedBranches ; do 107 | echo $'\t'$branch 108 | done 109 | 110 | if [ -z "$branches" ]; then 111 | echo "No branch matching $patterns were found" 112 | exit 0 113 | fi 114 | 115 | echo "Branches beeing merged :" 116 | for branch in $branches ; do 117 | echo $'\t'$branch 118 | done 119 | 120 | line_break 121 | 122 | mergeBases= sha1s= octopusMessage= i=0 123 | for branch in $branches 124 | do 125 | sha1=$(git rev-parse --verify "$branch") 126 | sha1s[$i]="$sha1" 127 | eval GITHEAD_$sha1='"$branch"' 128 | export GITHEAD_$sha1 129 | 130 | # merges bases are not used in the octopus stategy so we don't need to compute them 131 | # mergeBases="$mergeBases`git merge-base --all HEAD $branch` " 132 | 133 | octopusMessage[$i]="$branch" 134 | ((i++)) 135 | done 136 | $splitByChunk || chunkSize=${#sha1s[@]} 137 | 138 | $splitByChunk && echo "Will merge ${#sha1s[@]} branches by chunks of $chunkSize" 139 | for ((i=0; $i < ${#sha1s[@]}; i+=$chunkSize)) 140 | do 141 | if $splitByChunk; then 142 | upperChunk=$(($i + $chunkSize)) 143 | [ $upperChunk -gt ${#sha1s[@]} ] && upperChunk=${#sha1s[@]} 144 | echo "Merging chunks $i to $upperChunk (out of ${#sha1s[@]})" 145 | fi 146 | sha1sChunk=" ${sha1s[@]:$i:$chunkSize}" 147 | 148 | alreadyUpToDate=true 149 | for sha1 in ${sha1sChunk[@]} 150 | do 151 | git merge-base --is-ancestor $sha1 HEAD || alreadyUpToDate=false 152 | done 153 | 154 | # This prevents git-octopus to create a commit when there's nothing to merge, 155 | # i.e. no feature branches but only master. 156 | $alreadyUpToDate && octopusStatus=0 && echo "Already up to date" && continue 157 | 158 | merge-octopus-fork "$mergeBases" -- HEAD $sha1sChunk 159 | octopusStatus=$? 160 | if [ $octopusStatus -eq 0 ] 161 | then 162 | if [[ $doCommit || $splitByChunk ]]; then 163 | tree=$(git write-tree) 164 | head=$(git rev-parse --verify HEAD) 165 | octopusMessageChunk="${octopusMessage[@]:$i:$chunkSize} " 166 | commit=$(git commit-tree -p $head ${sha1sChunk// / -p } -m "${octopusMessageChunk// /$LF}" $tree) 167 | git update-ref HEAD $commit 168 | $splitByChunk && echo "Chunk success" 169 | fi 170 | else 171 | $splitByChunk && echo "Chunk failed" 172 | break 173 | fi 174 | done 175 | 176 | if [ $octopusStatus -eq 0 ] 177 | then 178 | if ! $doCommit; then 179 | git reset -q --hard $triggeredSha1 180 | fi 181 | line_break 182 | echo "OCTOPUS SUCCESS" 183 | else 184 | # Octopus merge failed, starting to run the analysis sequence ... 185 | line_break 186 | 187 | git reset -q --hard $triggeredSha1 188 | 189 | echo "Testing merges one by one with $triggeredBranch..." 190 | echo 191 | 192 | tmpFile= 193 | 194 | # Will perform a simple merge from the current branch with each branches one by one. 195 | for branch in $branches 196 | do 197 | if [[ $(git rev-parse $branch) != $(git rev-parse $triggeredBranch) ]] 198 | then 199 | echo -n "merging $branch ... " 200 | 201 | # merge base is not used in the octopus strategy 202 | # mergeBase=$(git merge-base --all HEAD $branch) 203 | mergeBase= 204 | 205 | sha1=$(git rev-parse --verify "$branch") 206 | 207 | tmpFile=$(merge-octopus-fork "$mergeBase" -- HEAD $sha1 2>&1) 208 | 209 | if [ $? -eq 0 ] 210 | then 211 | echo "SUCCESS" 212 | else 213 | echo "FAILED" 214 | echo "$tmpFile" 215 | git diff 216 | conflicts+="$branch " 217 | fi 218 | git reset -q --hard 219 | fi 220 | done 221 | 222 | line_break 223 | 224 | if [ -z "$conflicts" ]; then 225 | echo "No conflicts found between $triggeredBranch and the rest of the branches" 226 | else 227 | echo "$triggeredBranch has conflicts with :" 228 | for branch in $conflicts 229 | do 230 | echo $'\t'$branch 231 | done 232 | fi 233 | 234 | echo "OCTOPUS FAILED" 235 | exit 1 236 | fi 237 | -------------------------------------------------------------------------------- /src/lib/common: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | LF=' 3 | ' 4 | die () { 5 | echo >&2 "$*" 6 | exit 1 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/git-merge-octopus-fork.sh: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2005 Junio C Hamano 2 | # 3 | # Resolve two or more trees. 4 | # 5 | 6 | merge-octopus-fork () { 7 | # The first parameters up to -- are merge bases; the rest are heads. 8 | bases= head= remotes= sep_seen= 9 | for arg 10 | do 11 | case ",$sep_seen,$head,$arg," in 12 | *,--,) 13 | sep_seen=yes 14 | ;; 15 | ,yes,,*) 16 | head=$arg 17 | ;; 18 | ,yes,*) 19 | remotes="$remotes$arg " 20 | ;; 21 | *) 22 | bases="$bases$arg " 23 | ;; 24 | esac 25 | done 26 | 27 | # MRC is the current "merge reference commit" 28 | # MRT is the current "merge result tree" 29 | 30 | MRC=$(git rev-parse --verify -q $head) 31 | MRT=$(git write-tree) 32 | NON_FF_MERGE=0 33 | OCTOPUS_FAILURE=0 34 | for SHA1 in $remotes 35 | do 36 | case "$OCTOPUS_FAILURE" in 37 | 1) 38 | # We allow only last one to have a hand-resolvable 39 | # conflicts. Last round failed and we still had 40 | # a head to merge. 41 | echo "Automated merge did not work." 42 | echo "Should not be doing an Octopus." 43 | return 2 44 | esac 45 | 46 | eval pretty_name=\${GITHEAD_$SHA1:-$SHA1} 47 | if test "$SHA1" = "$pretty_name" 48 | then 49 | SHA1_UP="$(echo "$SHA1" | tr a-z A-Z)" 50 | eval pretty_name=\${GITHEAD_$SHA1_UP:-$pretty_name} 51 | fi 52 | common=$(git merge-base --all $SHA1 $MRC) || 53 | die "Unable to find common commit with $pretty_name" 54 | 55 | case "$LF$common$LF" in 56 | *"$LF$SHA1$LF"*) 57 | echo "Already up-to-date with $pretty_name" 58 | continue 59 | ;; 60 | esac 61 | 62 | if test "$common,$NON_FF_MERGE" = "$MRC,0" 63 | then 64 | # The first head being merged was a fast-forward. 65 | # Advance MRC to the head being merged, and use that 66 | # tree as the intermediate result of the merge. 67 | # We still need to count this as part of the parent set. 68 | 69 | echo "Fast-forwarding to: $pretty_name" 70 | git read-tree -u -m $head $SHA1 || return 0 71 | MRC=$SHA1 MRT=$(git write-tree) 72 | continue 73 | fi 74 | 75 | NON_FF_MERGE=1 76 | 77 | echo "Trying simple merge with $pretty_name" 78 | git read-tree -u -m --aggressive $common $MRT $SHA1 || return 2 79 | next=$(git write-tree 2>/dev/null) 80 | if test $? -ne 0 81 | then 82 | echo "Simple merge did not work, trying automatic merge." 83 | git-merge-index -o git-merge-one-file -a 84 | 85 | if test $? -ne 0 86 | then 87 | git apply-conflict-resolution || OCTOPUS_FAILURE=1 88 | fi 89 | 90 | next=$(git write-tree 2>/dev/null) 91 | fi 92 | 93 | MRC="$MRC $SHA1" 94 | MRT=$next 95 | done 96 | 97 | return "$OCTOPUS_FAILURE" 98 | } 99 | -------------------------------------------------------------------------------- /src/lib/hash-conflict: -------------------------------------------------------------------------------- 1 | cat-UU-conflict() { 2 | leftContent= 3 | rightContent= 4 | reading="base" 5 | 6 | while read l ; do 7 | [[ $l == \<\<\<\<\<\<\<* ]] && reading="left" && continue 8 | [[ $l == \=\=\=\=\=\=\=* ]] && reading="right" && continue 9 | [[ $l == \>\>\>\>\>\>\>* ]] && reading="base" && continue 10 | 11 | case $reading in 12 | "base") 13 | ;; 14 | "left") 15 | leftContent+="$l$LF" 16 | ;; 17 | "right") 18 | rightContent+="$l$LF" 19 | ;; 20 | esac 21 | done < "$1" 22 | 23 | conflictContent= 24 | if ! $ordered && [ "$leftContent" \< "$rightContent" ] ; then 25 | conflictContent="$leftContent$LF$rightContent" 26 | else 27 | conflictContent="$rightContent$LF$leftContent" 28 | fi 29 | 30 | echo "$conflictContent" 31 | } 32 | 33 | hash-conflict () { 34 | # -o Make the order between left and right side matters. 35 | # -i Read instead of conflicting paths in the index 36 | ordered=false 37 | file= 38 | while getopts "oi:h" opt; do 39 | case "$opt" in 40 | h) 41 | usage 42 | return 0 43 | ;; 44 | o) 45 | ordered=true 46 | ;; 47 | i) 48 | file=$OPTARG 49 | ;; 50 | *) 51 | usage 52 | return 1 53 | ;; 54 | esac 55 | done 56 | 57 | conflictContent= 58 | 59 | while read status ; do 60 | state=$(echo $status | awk '{print $1;}') 61 | path=$(echo $status | awk '{print $2;}') 62 | case $state in 63 | "UU") 64 | conflictContent+="$(cat-UU-conflict $path)$LF" 65 | ;; 66 | esac 67 | done <<< "$(if [ -z "$file" ] ; then echo "$(git status --porcelain | grep "^U.\|^.U\|^AA\|^DD" | awk -v cdup="$(git rev-parse --show-cdup)" '{print $1" "cdup$2;}')" ; else echo "UU $file" ; fi)" 68 | 69 | echo "$conflictContent" | shasum | cut -f 1 -d " " 70 | } 71 | -------------------------------------------------------------------------------- /test/empty_command_test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM lesfurets/octopus-tests:simple_merge_latest 2 | ADD test.sh /home/ 3 | ADD bin /usr/local/bin 4 | RUN chmod +x /home/test.sh 5 | WORKDIR /home/octopus-tests/ 6 | CMD /home/test.sh 7 | -------------------------------------------------------------------------------- /test/empty_command_test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #FIXME 4 | git status &> /dev/null 5 | 6 | #saving the initial state of the repository 7 | sha1=`git rev-parse HEAD` 8 | 9 | git octopus 10 | 11 | if [ $? -ne 0 ] ; then 12 | exit 1 13 | fi 14 | 15 | if [[ `git rev-parse HEAD` != $sha1 ]] ; then 16 | echo "should stayed at HEAD" 17 | exit 1 18 | fi 19 | 20 | if [[ -n `git diff-index HEAD` ]] ; then 21 | echo "repository should be clean" 22 | exit 1 23 | fi 24 | 25 | if [[ `git symbolic-ref HEAD` != "refs/heads/master" ]] ; then 26 | echo "Should be still on master" 27 | exit 1 28 | fi -------------------------------------------------------------------------------- /test/exclude_pattern_config_test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM lesfurets/octopus-tests:simple_merge_latest 2 | ADD test.sh /home/ 3 | ADD bin /usr/local/bin 4 | RUN chmod +x /home/test.sh 5 | WORKDIR /home/octopus-tests/ 6 | CMD /home/test.sh 7 | -------------------------------------------------------------------------------- /test/exclude_pattern_config_test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #FIXME 4 | git status &> /dev/null 5 | 6 | git checkout features/feat1 7 | git config octopus.pattern features/* 8 | git config --add octopus.pattern master 9 | 10 | git config octopus.excludePattern "features/feat3" 11 | 12 | git octopus 13 | merged=`git branch --merged` 14 | if [[ $merged != *feat1* ]] ; then 15 | exit 1 16 | fi 17 | if [[ $merged != *feat2* ]] ; then 18 | exit 1 19 | fi 20 | if [[ $merged == *feat3* ]] ; then 21 | exit 1 22 | fi 23 | if [[ $merged != *master* ]] ; then 24 | exit 1 25 | fi 26 | -------------------------------------------------------------------------------- /test/no_commit_config_test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM lesfurets/octopus-tests:simple_merge_latest 2 | ADD test.sh /home/ 3 | ADD bin /usr/local/bin 4 | RUN chmod +x /home/test.sh 5 | WORKDIR /home/octopus-tests/ 6 | CMD /home/test.sh 7 | -------------------------------------------------------------------------------- /test/no_commit_config_test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #FIXME 4 | git status &> /dev/null 5 | 6 | git config octopus.commit false 7 | #saving the initial state of the repository 8 | sha1=`git rev-parse HEAD` 9 | 10 | git octopus features/* 11 | 12 | #should be back to HEAD 13 | if [[ `git rev-parse HEAD` != $sha1 ]] ; then 14 | exit 1 15 | fi 16 | 17 | #repository should be clean 18 | if [[ -n `git diff-index HEAD` ]] ; then 19 | exit 1 20 | fi 21 | 22 | #should be still on master 23 | if [[ `git symbolic-ref HEAD` != "refs/heads/master" ]] ; then 24 | echo "Should be still on master" 25 | exit 1 26 | fi -------------------------------------------------------------------------------- /test/no_commit_detached_test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM lesfurets/octopus-tests:simple_merge_latest 2 | ADD test.sh /home/ 3 | ADD bin /usr/local/bin 4 | RUN chmod +x /home/test.sh 5 | WORKDIR /home/octopus-tests/ 6 | CMD /home/test.sh 7 | -------------------------------------------------------------------------------- /test/no_commit_detached_test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | git status &> /dev/null 3 | 4 | git checkout --detach 5 | 6 | sha1=`git rev-parse HEAD` 7 | 8 | git octopus -n features/* 9 | 10 | #should be back to HEAD 11 | if [[ `git rev-parse HEAD` != $sha1 ]] ; then 12 | exit 1 13 | fi 14 | 15 | #repository should be clean 16 | if [[ -n `git diff-index HEAD` ]] ; then 17 | exit 1 18 | fi 19 | 20 | #should be still detached 21 | git symbolic-ref HEAD &> /dev/null 22 | 23 | if [ $? -eq 0 ] ; then 24 | echo "Repository should remains detached" 25 | exit 1 26 | fi -------------------------------------------------------------------------------- /test/no_commit_test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM lesfurets/octopus-tests:simple_merge_latest 2 | ADD test.sh /home/ 3 | ADD bin /usr/local/bin 4 | RUN chmod +x /home/test.sh 5 | WORKDIR /home/octopus-tests/ 6 | CMD /home/test.sh 7 | -------------------------------------------------------------------------------- /test/no_commit_test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #FIXME 4 | git status &> /dev/null 5 | 6 | #saving the initial state of the repository 7 | sha1=`git rev-parse HEAD` 8 | 9 | git octopus -n features/* 10 | 11 | #should be back to HEAD 12 | if [[ `git rev-parse HEAD` != $sha1 ]] ; then 13 | exit 1 14 | fi 15 | 16 | #repository should be clean 17 | if [[ -n `git diff-index HEAD` ]] ; then 18 | exit 1 19 | fi 20 | 21 | #should be still on master 22 | if [[ `git symbolic-ref HEAD` != "refs/heads/master" ]] ; then 23 | echo "Should be still on master" 24 | exit 1 25 | fi -------------------------------------------------------------------------------- /test/octopus_failed_test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM lesfurets/octopus-tests:merge_failure_latest 2 | ADD test.sh /home/ 3 | ADD bin /usr/local/bin 4 | RUN chmod +x /home/test.sh 5 | WORKDIR /home/octopus-tests/ 6 | CMD /home/test.sh -------------------------------------------------------------------------------- /test/octopus_failed_test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #FIXME 4 | git status &> /dev/null 5 | 6 | sha1=`git rev-parse HEAD` 7 | 8 | git octopus -n features/* 9 | 10 | if [ $? -ne 1 ] ; then 11 | exit 1 12 | fi 13 | 14 | #should be back to HEAD 15 | if [[ `git rev-parse HEAD` != $sha1 ]] ; then 16 | exit 1 17 | fi 18 | 19 | #repository should be clean 20 | if [[ -n `git diff-index HEAD` ]] ; then 21 | exit 1 22 | fi -------------------------------------------------------------------------------- /test/pattern_config_test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM lesfurets/octopus-tests:simple_merge_latest 2 | ADD test.sh /home/ 3 | ADD bin /usr/local/bin 4 | RUN chmod +x /home/test.sh 5 | WORKDIR /home/octopus-tests/ 6 | CMD /home/test.sh 7 | -------------------------------------------------------------------------------- /test/pattern_config_test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #FIXME 4 | git status &> /dev/null 5 | 6 | git checkout features/feat1 7 | git config octopus.pattern features/* 8 | git config --add octopus.pattern master 9 | 10 | git octopus 11 | merged=`git branch --merged` 12 | if [[ $merged != *feat1* ]] ; then 13 | exit 1 14 | fi 15 | if [[ $merged != *feat2* ]] ; then 16 | exit 1 17 | fi 18 | if [[ $merged != *feat3* ]] ; then 19 | exit 1 20 | fi 21 | if [[ $merged != *master* ]] ; then 22 | exit 1 23 | fi -------------------------------------------------------------------------------- /test/record_conflicts_test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM lesfurets/octopus-tests:merge_failure_latest 2 | ADD test.sh /home/ 3 | ADD bin /usr/local/bin 4 | RUN chmod +x /home/test.sh 5 | WORKDIR /home/octopus-tests/ 6 | CMD /home/test.sh -------------------------------------------------------------------------------- /test/record_conflicts_test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #FIXME 4 | git status &> /dev/null 5 | 6 | mergeBase=$(git merge-base --all HEAD features/feat1) 7 | git read-tree -um --aggressive $mergeBase HEAD features/feat1 8 | git merge-index -o -q git-merge-one-file -a 9 | 10 | git store-conflict -------------------------------------------------------------------------------- /test/run_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | testDir=`dirname $0` 3 | tests=`ls -1d $testDir/*_test` 4 | for test in $tests; do 5 | $testDir/run_test.sh "$test" 6 | echo "===========================================================" 7 | done -------------------------------------------------------------------------------- /test/run_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | bold="\\033[1m" 3 | green="\\033[1;32m" 4 | red="\\033[1;31m" 5 | normal="\\033[0m" 6 | 7 | testDir=`dirname $0` 8 | dockerImage=`basename $1` 9 | 10 | echo -e "Executing test ${bold}${dockerImage}${normal}..." 11 | 12 | #Copy bin sources into the docker build context. 13 | #see https://docs.docker.com/reference/builder/#add 14 | cp -Rf $testDir/../bin $1 15 | 16 | #Build a docker image for the test 17 | docker build -t $dockerImage $1 &> /dev/null 18 | echo 19 | 20 | #Run the test within a container 21 | docker run --cidfile="cid" -i $dockerImage 22 | cid=`cat cid` 23 | #Exit code of the container represent the test result 24 | exit=`docker wait "$cid"` 25 | 26 | #Cleanup cid file, docker container and docker image 27 | rm cid 28 | docker rm "$cid" 1> /dev/null 29 | docker rmi -f $dockerImage 1> /dev/null 30 | 31 | echo 32 | 33 | #Print test result 34 | if [ $exit == 0 ] 35 | then 36 | echo -e "${green}$dockerImage SUCCESS${normal}" 37 | else 38 | echo -e "${red}$dockerImage FAILED${normal}" 39 | exit 1 40 | fi 41 | 42 | -------------------------------------------------------------------------------- /test/simple_test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM lesfurets/octopus-tests:simple_merge_latest 2 | ADD test.sh /home/ 3 | ADD bin /usr/local/bin 4 | RUN chmod +x /home/test.sh 5 | WORKDIR /home/octopus-tests/ 6 | CMD /home/test.sh 7 | -------------------------------------------------------------------------------- /test/simple_test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #FIXME 4 | git status &> /dev/null 5 | 6 | git octopus features/* 7 | merged=`git branch --merged` 8 | if [[ $merged != *feat1* ]] ; then 9 | exit 1 10 | fi 11 | if [[ $merged != *feat2* ]] ; then 12 | exit 1 13 | fi 14 | if [[ $merged != *feat3* ]] ; then 15 | exit 1 16 | fi 17 | if [[ $merged != *master* ]] ; then 18 | exit 1 19 | fi -------------------------------------------------------------------------------- /test/single_parent_merge_test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM lesfurets/octopus-tests:simple_merge_latest 2 | ADD test.sh /home/ 3 | ADD bin /usr/local/bin 4 | RUN chmod +x /home/test.sh 5 | WORKDIR /home/octopus-tests/ 6 | CMD /home/test.sh 7 | -------------------------------------------------------------------------------- /test/single_parent_merge_test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #FIXME 4 | git status &> /dev/null 5 | 6 | git branch -D features/feat2 features/feat3 7 | git branch features/feat0 8 | 9 | git octopus features/* 10 | merged=`git branch --merged` 11 | if [[ $merged != *feat0* ]] ; then 12 | exit 1 13 | fi 14 | if [[ $merged != *feat1* ]] ; then 15 | exit 1 16 | fi 17 | if [[ $merged != *master* ]] ; then 18 | exit 1 19 | fi 20 | -------------------------------------------------------------------------------- /test/test_utils.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/lesfurets/git-octopus/git" 5 | "io/ioutil" 6 | "os" 7 | ) 8 | 9 | func CreateTempDir() string { 10 | dir, _ := ioutil.TempDir("", "git-octopus-test-") 11 | return dir 12 | } 13 | 14 | func Cleanup(repo *git.Repository) error { 15 | return os.RemoveAll(repo.Path) 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/LICENCE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell 2 | 3 | Please consider promoting this project if you find it useful. 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 21 | OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 22 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell 2 | 3 | Please consider promoting this project if you find it useful. 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 21 | OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 22 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/doc.go: -------------------------------------------------------------------------------- 1 | // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. 2 | // 3 | // Example Usage 4 | // 5 | // The following is a complete example using assert in a standard test function: 6 | // import ( 7 | // "testing" 8 | // "github.com/stretchr/testify/assert" 9 | // ) 10 | // 11 | // func TestSomething(t *testing.T) { 12 | // 13 | // var a string = "Hello" 14 | // var b string = "Hello" 15 | // 16 | // assert.Equal(t, a, b, "The two words should be the same.") 17 | // 18 | // } 19 | // 20 | // if you assert many times, use the format below: 21 | // 22 | // import ( 23 | // "testing" 24 | // "github.com/stretchr/testify/assert" 25 | // ) 26 | // 27 | // func TestSomething(t *testing.T) { 28 | // assert := assert.New(t) 29 | // 30 | // var a string = "Hello" 31 | // var b string = "Hello" 32 | // 33 | // assert.Equal(a, b, "The two words should be the same.") 34 | // } 35 | // 36 | // Assertions 37 | // 38 | // Assertions allow you to easily write test code, and are global funcs in the `assert` package. 39 | // All assertion functions take, as the first argument, the `*testing.T` object provided by the 40 | // testing framework. This allows the assertion funcs to write the failings and other details to 41 | // the correct place. 42 | // 43 | // Every assertion function also takes an optional string message as the final argument, 44 | // allowing custom error messages to be appended to the message the assertion method outputs. 45 | package assert 46 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/errors.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // AnError is an error instance useful for testing. If the code does not care 8 | // about error specifics, and only needs to return the error for example, this 9 | // error should be used to make the test code more readable. 10 | var AnError = errors.New("assert.AnError general error for testing") 11 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/forward_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | // Assertions provides assertion methods around the 4 | // TestingT interface. 5 | type Assertions struct { 6 | t TestingT 7 | } 8 | 9 | // New makes a new Assertions object for the specified TestingT. 10 | func New(t TestingT) *Assertions { 11 | return &Assertions{ 12 | t: t, 13 | } 14 | } 15 | 16 | //go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl 17 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/http_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "net/url" 8 | "strings" 9 | ) 10 | 11 | // httpCode is a helper that returns HTTP code of the response. It returns -1 12 | // if building a new request fails. 13 | func httpCode(handler http.HandlerFunc, method, url string, values url.Values) int { 14 | w := httptest.NewRecorder() 15 | req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) 16 | if err != nil { 17 | return -1 18 | } 19 | handler(w, req) 20 | return w.Code 21 | } 22 | 23 | // HTTPSuccess asserts that a specified handler returns a success status code. 24 | // 25 | // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) 26 | // 27 | // Returns whether the assertion was successful (true) or not (false). 28 | func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { 29 | code := httpCode(handler, method, url, values) 30 | if code == -1 { 31 | return false 32 | } 33 | return code >= http.StatusOK && code <= http.StatusPartialContent 34 | } 35 | 36 | // HTTPRedirect asserts that a specified handler returns a redirect status code. 37 | // 38 | // assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} 39 | // 40 | // Returns whether the assertion was successful (true) or not (false). 41 | func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { 42 | code := httpCode(handler, method, url, values) 43 | if code == -1 { 44 | return false 45 | } 46 | return code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect 47 | } 48 | 49 | // HTTPError asserts that a specified handler returns an error status code. 50 | // 51 | // assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} 52 | // 53 | // Returns whether the assertion was successful (true) or not (false). 54 | func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { 55 | code := httpCode(handler, method, url, values) 56 | if code == -1 { 57 | return false 58 | } 59 | return code >= http.StatusBadRequest 60 | } 61 | 62 | // HTTPBody is a helper that returns HTTP body of the response. It returns 63 | // empty string if building a new request fails. 64 | func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { 65 | w := httptest.NewRecorder() 66 | req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) 67 | if err != nil { 68 | return "" 69 | } 70 | handler(w, req) 71 | return w.Body.String() 72 | } 73 | 74 | // HTTPBodyContains asserts that a specified handler returns a 75 | // body that contains a string. 76 | // 77 | // assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") 78 | // 79 | // Returns whether the assertion was successful (true) or not (false). 80 | func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { 81 | body := HTTPBody(handler, method, url, values) 82 | 83 | contains := strings.Contains(body, fmt.Sprint(str)) 84 | if !contains { 85 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) 86 | } 87 | 88 | return contains 89 | } 90 | 91 | // HTTPBodyNotContains asserts that a specified handler returns a 92 | // body that does not contain a string. 93 | // 94 | // assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") 95 | // 96 | // Returns whether the assertion was successful (true) or not (false). 97 | func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { 98 | body := HTTPBody(handler, method, url, values) 99 | 100 | contains := strings.Contains(body, fmt.Sprint(str)) 101 | if contains { 102 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) 103 | } 104 | 105 | return !contains 106 | } 107 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/doc.go: -------------------------------------------------------------------------------- 1 | // Package testify is a set of packages that provide many tools for testifying that your code will behave as you intend. 2 | // 3 | // testify contains the following packages: 4 | // 5 | // The assert package provides a comprehensive set of assertion functions that tie in to the Go testing system. 6 | // 7 | // The http package contains tools to make it easier to test http activity using the Go testing system. 8 | // 9 | // The mock package provides a system by which it is possible to mock your objects and verify calls are happening as expected. 10 | // 11 | // The suite package provides a basic structure for using structs as testing suites, and methods on those structs as tests. It includes setup/teardown functionality in the way of interfaces. 12 | package testify 13 | 14 | // blank imports help docs. 15 | import ( 16 | // assert package 17 | _ "github.com/stretchr/testify/assert" 18 | // http package 19 | _ "github.com/stretchr/testify/http" 20 | // mock package 21 | _ "github.com/stretchr/testify/mock" 22 | ) 23 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/http/doc.go: -------------------------------------------------------------------------------- 1 | // Package http DEPRECATED USE net/http/httptest 2 | package http 3 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/http/test_response_writer.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | // TestResponseWriter DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. 8 | type TestResponseWriter struct { 9 | 10 | // StatusCode is the last int written by the call to WriteHeader(int) 11 | StatusCode int 12 | 13 | // Output is a string containing the written bytes using the Write([]byte) func. 14 | Output string 15 | 16 | // header is the internal storage of the http.Header object 17 | header http.Header 18 | } 19 | 20 | // Header DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. 21 | func (rw *TestResponseWriter) Header() http.Header { 22 | 23 | if rw.header == nil { 24 | rw.header = make(http.Header) 25 | } 26 | 27 | return rw.header 28 | } 29 | 30 | // Write DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. 31 | func (rw *TestResponseWriter) Write(bytes []byte) (int, error) { 32 | 33 | // assume 200 success if no header has been set 34 | if rw.StatusCode == 0 { 35 | rw.WriteHeader(200) 36 | } 37 | 38 | // add these bytes to the output string 39 | rw.Output = rw.Output + string(bytes) 40 | 41 | // return normal values 42 | return 0, nil 43 | 44 | } 45 | 46 | // WriteHeader DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead. 47 | func (rw *TestResponseWriter) WriteHeader(i int) { 48 | rw.StatusCode = i 49 | } 50 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/http/test_round_tripper.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/stretchr/testify/mock" 5 | "net/http" 6 | ) 7 | 8 | // TestRoundTripper DEPRECATED USE net/http/httptest 9 | type TestRoundTripper struct { 10 | mock.Mock 11 | } 12 | 13 | // RoundTrip DEPRECATED USE net/http/httptest 14 | func (t *TestRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { 15 | args := t.Called(req) 16 | return args.Get(0).(*http.Response), args.Error(1) 17 | } 18 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/mock/doc.go: -------------------------------------------------------------------------------- 1 | // Package mock provides a system by which it is possible to mock your objects 2 | // and verify calls are happening as expected. 3 | // 4 | // Example Usage 5 | // 6 | // The mock package provides an object, Mock, that tracks activity on another object. It is usually 7 | // embedded into a test object as shown below: 8 | // 9 | // type MyTestObject struct { 10 | // // add a Mock object instance 11 | // mock.Mock 12 | // 13 | // // other fields go here as normal 14 | // } 15 | // 16 | // When implementing the methods of an interface, you wire your functions up 17 | // to call the Mock.Called(args...) method, and return the appropriate values. 18 | // 19 | // For example, to mock a method that saves the name and age of a person and returns 20 | // the year of their birth or an error, you might write this: 21 | // 22 | // func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { 23 | // args := o.Called(firstname, lastname, age) 24 | // return args.Int(0), args.Error(1) 25 | // } 26 | // 27 | // The Int, Error and Bool methods are examples of strongly typed getters that take the argument 28 | // index position. Given this argument list: 29 | // 30 | // (12, true, "Something") 31 | // 32 | // You could read them out strongly typed like this: 33 | // 34 | // args.Int(0) 35 | // args.Bool(1) 36 | // args.String(2) 37 | // 38 | // For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion: 39 | // 40 | // return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) 41 | // 42 | // This may cause a panic if the object you are getting is nil (the type assertion will fail), in those 43 | // cases you should check for nil first. 44 | package mock 45 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/require/doc.go: -------------------------------------------------------------------------------- 1 | // Package require implements the same assertions as the `assert` package but 2 | // stops test execution when a test fails. 3 | // 4 | // Example Usage 5 | // 6 | // The following is a complete example using require in a standard test function: 7 | // import ( 8 | // "testing" 9 | // "github.com/stretchr/testify/require" 10 | // ) 11 | // 12 | // func TestSomething(t *testing.T) { 13 | // 14 | // var a string = "Hello" 15 | // var b string = "Hello" 16 | // 17 | // require.Equal(t, a, b, "The two words should be the same.") 18 | // 19 | // } 20 | // 21 | // Assertions 22 | // 23 | // The `require` package have same global functions as in the `assert` package, 24 | // but instead of returning a boolean result they call `t.FailNow()`. 25 | // 26 | // Every assertion function also takes an optional string message as the final argument, 27 | // allowing custom error messages to be appended to the message the assertion method outputs. 28 | package require 29 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/require/forward_requirements.go: -------------------------------------------------------------------------------- 1 | package require 2 | 3 | // Assertions provides assertion methods around the 4 | // TestingT interface. 5 | type Assertions struct { 6 | t TestingT 7 | } 8 | 9 | // New makes a new Assertions object for the specified TestingT. 10 | func New(t TestingT) *Assertions { 11 | return &Assertions{ 12 | t: t, 13 | } 14 | } 15 | 16 | //go:generate go run ../_codegen/main.go -output-package=require -template=require_forward.go.tmpl 17 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/require/require_forward.go: -------------------------------------------------------------------------------- 1 | /* 2 | * CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen 3 | * THIS FILE MUST NOT BE EDITED BY HAND 4 | */ 5 | 6 | package require 7 | 8 | import ( 9 | assert "github.com/stretchr/testify/assert" 10 | http "net/http" 11 | url "net/url" 12 | time "time" 13 | ) 14 | 15 | // Condition uses a Comparison to assert a complex condition. 16 | func (a *Assertions) Condition(comp assert.Comparison, msgAndArgs ...interface{}) { 17 | Condition(a.t, comp, msgAndArgs...) 18 | } 19 | 20 | // Contains asserts that the specified string, list(array, slice...) or map contains the 21 | // specified substring or element. 22 | // 23 | // a.Contains("Hello World", "World", "But 'Hello World' does contain 'World'") 24 | // a.Contains(["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") 25 | // a.Contains({"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'") 26 | // 27 | // Returns whether the assertion was successful (true) or not (false). 28 | func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { 29 | Contains(a.t, s, contains, msgAndArgs...) 30 | } 31 | 32 | // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either 33 | // a slice or a channel with len == 0. 34 | // 35 | // a.Empty(obj) 36 | // 37 | // Returns whether the assertion was successful (true) or not (false). 38 | func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { 39 | Empty(a.t, object, msgAndArgs...) 40 | } 41 | 42 | // Equal asserts that two objects are equal. 43 | // 44 | // a.Equal(123, 123, "123 and 123 should be equal") 45 | // 46 | // Returns whether the assertion was successful (true) or not (false). 47 | // 48 | // Pointer variable equality is determined based on the equality of the 49 | // referenced values (as opposed to the memory addresses). 50 | func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { 51 | Equal(a.t, expected, actual, msgAndArgs...) 52 | } 53 | 54 | // EqualError asserts that a function returned an error (i.e. not `nil`) 55 | // and that it is equal to the provided error. 56 | // 57 | // actualObj, err := SomeFunction() 58 | // a.EqualError(err, expectedErrorString, "An error was expected") 59 | // 60 | // Returns whether the assertion was successful (true) or not (false). 61 | func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) { 62 | EqualError(a.t, theError, errString, msgAndArgs...) 63 | } 64 | 65 | // EqualValues asserts that two objects are equal or convertable to the same types 66 | // and equal. 67 | // 68 | // a.EqualValues(uint32(123), int32(123), "123 and 123 should be equal") 69 | // 70 | // Returns whether the assertion was successful (true) or not (false). 71 | func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { 72 | EqualValues(a.t, expected, actual, msgAndArgs...) 73 | } 74 | 75 | // Error asserts that a function returned an error (i.e. not `nil`). 76 | // 77 | // actualObj, err := SomeFunction() 78 | // if a.Error(err, "An error was expected") { 79 | // assert.Equal(t, err, expectedError) 80 | // } 81 | // 82 | // Returns whether the assertion was successful (true) or not (false). 83 | func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { 84 | Error(a.t, err, msgAndArgs...) 85 | } 86 | 87 | // Exactly asserts that two objects are equal is value and type. 88 | // 89 | // a.Exactly(int32(123), int64(123), "123 and 123 should NOT be equal") 90 | // 91 | // Returns whether the assertion was successful (true) or not (false). 92 | func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { 93 | Exactly(a.t, expected, actual, msgAndArgs...) 94 | } 95 | 96 | // Fail reports a failure through 97 | func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) { 98 | Fail(a.t, failureMessage, msgAndArgs...) 99 | } 100 | 101 | // FailNow fails test 102 | func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) { 103 | FailNow(a.t, failureMessage, msgAndArgs...) 104 | } 105 | 106 | // False asserts that the specified value is false. 107 | // 108 | // a.False(myBool, "myBool should be false") 109 | // 110 | // Returns whether the assertion was successful (true) or not (false). 111 | func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { 112 | False(a.t, value, msgAndArgs...) 113 | } 114 | 115 | // HTTPBodyContains asserts that a specified handler returns a 116 | // body that contains a string. 117 | // 118 | // a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") 119 | // 120 | // Returns whether the assertion was successful (true) or not (false). 121 | func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) { 122 | HTTPBodyContains(a.t, handler, method, url, values, str) 123 | } 124 | 125 | // HTTPBodyNotContains asserts that a specified handler returns a 126 | // body that does not contain a string. 127 | // 128 | // a.HTTPBodyNotContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") 129 | // 130 | // Returns whether the assertion was successful (true) or not (false). 131 | func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) { 132 | HTTPBodyNotContains(a.t, handler, method, url, values, str) 133 | } 134 | 135 | // HTTPError asserts that a specified handler returns an error status code. 136 | // 137 | // a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} 138 | // 139 | // Returns whether the assertion was successful (true) or not (false). 140 | func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) { 141 | HTTPError(a.t, handler, method, url, values) 142 | } 143 | 144 | // HTTPRedirect asserts that a specified handler returns a redirect status code. 145 | // 146 | // a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} 147 | // 148 | // Returns whether the assertion was successful (true) or not (false). 149 | func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) { 150 | HTTPRedirect(a.t, handler, method, url, values) 151 | } 152 | 153 | // HTTPSuccess asserts that a specified handler returns a success status code. 154 | // 155 | // a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) 156 | // 157 | // Returns whether the assertion was successful (true) or not (false). 158 | func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) { 159 | HTTPSuccess(a.t, handler, method, url, values) 160 | } 161 | 162 | // Implements asserts that an object is implemented by the specified interface. 163 | // 164 | // a.Implements((*MyInterface)(nil), new(MyObject), "MyObject") 165 | func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { 166 | Implements(a.t, interfaceObject, object, msgAndArgs...) 167 | } 168 | 169 | // InDelta asserts that the two numerals are within delta of each other. 170 | // 171 | // a.InDelta(math.Pi, (22 / 7.0), 0.01) 172 | // 173 | // Returns whether the assertion was successful (true) or not (false). 174 | func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { 175 | InDelta(a.t, expected, actual, delta, msgAndArgs...) 176 | } 177 | 178 | // InDeltaSlice is the same as InDelta, except it compares two slices. 179 | func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { 180 | InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) 181 | } 182 | 183 | // InEpsilon asserts that expected and actual have a relative error less than epsilon 184 | // 185 | // Returns whether the assertion was successful (true) or not (false). 186 | func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { 187 | InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) 188 | } 189 | 190 | // InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. 191 | func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { 192 | InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) 193 | } 194 | 195 | // IsType asserts that the specified objects are of the same type. 196 | func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { 197 | IsType(a.t, expectedType, object, msgAndArgs...) 198 | } 199 | 200 | // JSONEq asserts that two JSON strings are equivalent. 201 | // 202 | // a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) 203 | // 204 | // Returns whether the assertion was successful (true) or not (false). 205 | func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) { 206 | JSONEq(a.t, expected, actual, msgAndArgs...) 207 | } 208 | 209 | // Len asserts that the specified object has specific length. 210 | // Len also fails if the object has a type that len() not accept. 211 | // 212 | // a.Len(mySlice, 3, "The size of slice is not 3") 213 | // 214 | // Returns whether the assertion was successful (true) or not (false). 215 | func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) { 216 | Len(a.t, object, length, msgAndArgs...) 217 | } 218 | 219 | // Nil asserts that the specified object is nil. 220 | // 221 | // a.Nil(err, "err should be nothing") 222 | // 223 | // Returns whether the assertion was successful (true) or not (false). 224 | func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { 225 | Nil(a.t, object, msgAndArgs...) 226 | } 227 | 228 | // NoError asserts that a function returned no error (i.e. `nil`). 229 | // 230 | // actualObj, err := SomeFunction() 231 | // if a.NoError(err) { 232 | // assert.Equal(t, actualObj, expectedObj) 233 | // } 234 | // 235 | // Returns whether the assertion was successful (true) or not (false). 236 | func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { 237 | NoError(a.t, err, msgAndArgs...) 238 | } 239 | 240 | // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the 241 | // specified substring or element. 242 | // 243 | // a.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") 244 | // a.NotContains(["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") 245 | // a.NotContains({"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'") 246 | // 247 | // Returns whether the assertion was successful (true) or not (false). 248 | func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { 249 | NotContains(a.t, s, contains, msgAndArgs...) 250 | } 251 | 252 | // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either 253 | // a slice or a channel with len == 0. 254 | // 255 | // if a.NotEmpty(obj) { 256 | // assert.Equal(t, "two", obj[1]) 257 | // } 258 | // 259 | // Returns whether the assertion was successful (true) or not (false). 260 | func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { 261 | NotEmpty(a.t, object, msgAndArgs...) 262 | } 263 | 264 | // NotEqual asserts that the specified values are NOT equal. 265 | // 266 | // a.NotEqual(obj1, obj2, "two objects shouldn't be equal") 267 | // 268 | // Returns whether the assertion was successful (true) or not (false). 269 | // 270 | // Pointer variable equality is determined based on the equality of the 271 | // referenced values (as opposed to the memory addresses). 272 | func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { 273 | NotEqual(a.t, expected, actual, msgAndArgs...) 274 | } 275 | 276 | // NotNil asserts that the specified object is not nil. 277 | // 278 | // a.NotNil(err, "err should be something") 279 | // 280 | // Returns whether the assertion was successful (true) or not (false). 281 | func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { 282 | NotNil(a.t, object, msgAndArgs...) 283 | } 284 | 285 | // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. 286 | // 287 | // a.NotPanics(func(){ 288 | // RemainCalm() 289 | // }, "Calling RemainCalm() should NOT panic") 290 | // 291 | // Returns whether the assertion was successful (true) or not (false). 292 | func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { 293 | NotPanics(a.t, f, msgAndArgs...) 294 | } 295 | 296 | // NotRegexp asserts that a specified regexp does not match a string. 297 | // 298 | // a.NotRegexp(regexp.MustCompile("starts"), "it's starting") 299 | // a.NotRegexp("^start", "it's not starting") 300 | // 301 | // Returns whether the assertion was successful (true) or not (false). 302 | func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { 303 | NotRegexp(a.t, rx, str, msgAndArgs...) 304 | } 305 | 306 | // NotZero asserts that i is not the zero value for its type and returns the truth. 307 | func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) { 308 | NotZero(a.t, i, msgAndArgs...) 309 | } 310 | 311 | // Panics asserts that the code inside the specified PanicTestFunc panics. 312 | // 313 | // a.Panics(func(){ 314 | // GoCrazy() 315 | // }, "Calling GoCrazy() should panic") 316 | // 317 | // Returns whether the assertion was successful (true) or not (false). 318 | func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { 319 | Panics(a.t, f, msgAndArgs...) 320 | } 321 | 322 | // Regexp asserts that a specified regexp matches a string. 323 | // 324 | // a.Regexp(regexp.MustCompile("start"), "it's starting") 325 | // a.Regexp("start...$", "it's not starting") 326 | // 327 | // Returns whether the assertion was successful (true) or not (false). 328 | func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { 329 | Regexp(a.t, rx, str, msgAndArgs...) 330 | } 331 | 332 | // True asserts that the specified value is true. 333 | // 334 | // a.True(myBool, "myBool should be true") 335 | // 336 | // Returns whether the assertion was successful (true) or not (false). 337 | func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { 338 | True(a.t, value, msgAndArgs...) 339 | } 340 | 341 | // WithinDuration asserts that the two times are within duration delta of each other. 342 | // 343 | // a.WithinDuration(time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") 344 | // 345 | // Returns whether the assertion was successful (true) or not (false). 346 | func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { 347 | WithinDuration(a.t, expected, actual, delta, msgAndArgs...) 348 | } 349 | 350 | // Zero asserts that i is the zero value for its type and returns the truth. 351 | func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) { 352 | Zero(a.t, i, msgAndArgs...) 353 | } 354 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/require/requirements.go: -------------------------------------------------------------------------------- 1 | package require 2 | 3 | // TestingT is an interface wrapper around *testing.T 4 | type TestingT interface { 5 | Errorf(format string, args ...interface{}) 6 | FailNow() 7 | } 8 | 9 | //go:generate go run ../_codegen/main.go -output-package=require -template=require.go.tmpl 10 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/suite/doc.go: -------------------------------------------------------------------------------- 1 | // Package suite contains logic for creating testing suite structs 2 | // and running the methods on those structs as tests. The most useful 3 | // piece of this package is that you can create setup/teardown methods 4 | // on your testing suites, which will run before/after the whole suite 5 | // or individual tests (depending on which interface(s) you 6 | // implement). 7 | // 8 | // A testing suite is usually built by first extending the built-in 9 | // suite functionality from suite.Suite in testify. Alternatively, 10 | // you could reproduce that logic on your own if you wanted (you 11 | // just need to implement the TestingSuite interface from 12 | // suite/interfaces.go). 13 | // 14 | // After that, you can implement any of the interfaces in 15 | // suite/interfaces.go to add setup/teardown functionality to your 16 | // suite, and add any methods that start with "Test" to add tests. 17 | // Methods that do not match any suite interfaces and do not begin 18 | // with "Test" will not be run by testify, and can safely be used as 19 | // helper methods. 20 | // 21 | // Once you've built your testing suite, you need to run the suite 22 | // (using suite.Run from testify) inside any function that matches the 23 | // identity that "go test" is already looking for (i.e. 24 | // func(*testing.T)). 25 | // 26 | // Regular expression to select test suites specified command-line 27 | // argument "-run". Regular expression to select the methods 28 | // of test suites specified command-line argument "-m". 29 | // Suite object has assertion methods. 30 | // 31 | // A crude example: 32 | // // Basic imports 33 | // import ( 34 | // "testing" 35 | // "github.com/stretchr/testify/assert" 36 | // "github.com/stretchr/testify/suite" 37 | // ) 38 | // 39 | // // Define the suite, and absorb the built-in basic suite 40 | // // functionality from testify - including a T() method which 41 | // // returns the current testing context 42 | // type ExampleTestSuite struct { 43 | // suite.Suite 44 | // VariableThatShouldStartAtFive int 45 | // } 46 | // 47 | // // Make sure that VariableThatShouldStartAtFive is set to five 48 | // // before each test 49 | // func (suite *ExampleTestSuite) SetupTest() { 50 | // suite.VariableThatShouldStartAtFive = 5 51 | // } 52 | // 53 | // // All methods that begin with "Test" are run as tests within a 54 | // // suite. 55 | // func (suite *ExampleTestSuite) TestExample() { 56 | // assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) 57 | // suite.Equal(5, suite.VariableThatShouldStartAtFive) 58 | // } 59 | // 60 | // // In order for 'go test' to run this suite, we need to create 61 | // // a normal test function and pass our suite to suite.Run 62 | // func TestExampleTestSuite(t *testing.T) { 63 | // suite.Run(t, new(ExampleTestSuite)) 64 | // } 65 | package suite 66 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/suite/interfaces.go: -------------------------------------------------------------------------------- 1 | package suite 2 | 3 | import "testing" 4 | 5 | // TestingSuite can store and return the current *testing.T context 6 | // generated by 'go test'. 7 | type TestingSuite interface { 8 | T() *testing.T 9 | SetT(*testing.T) 10 | } 11 | 12 | // SetupAllSuite has a SetupSuite method, which will run before the 13 | // tests in the suite are run. 14 | type SetupAllSuite interface { 15 | SetupSuite() 16 | } 17 | 18 | // SetupTestSuite has a SetupTest method, which will run before each 19 | // test in the suite. 20 | type SetupTestSuite interface { 21 | SetupTest() 22 | } 23 | 24 | // TearDownAllSuite has a TearDownSuite method, which will run after 25 | // all the tests in the suite have been run. 26 | type TearDownAllSuite interface { 27 | TearDownSuite() 28 | } 29 | 30 | // TearDownTestSuite has a TearDownTest method, which will run after 31 | // each test in the suite. 32 | type TearDownTestSuite interface { 33 | TearDownTest() 34 | } 35 | 36 | // BeforeTest has a function to be executed right before the test 37 | // starts and receives the suite and test names as input 38 | type BeforeTest interface { 39 | BeforeTest(suiteName, testName string) 40 | } 41 | 42 | // AfterTest has a function to be executed right after the test 43 | // finishes and receives the suite and test names as input 44 | type AfterTest interface { 45 | AfterTest(suiteName, testName string) 46 | } 47 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/suite/suite.go: -------------------------------------------------------------------------------- 1 | package suite 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "reflect" 8 | "regexp" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | var matchMethod = flag.String("testify.m", "", "regular expression to select tests of the testify suite to run") 16 | 17 | // Suite is a basic testing suite with methods for storing and 18 | // retrieving the current *testing.T context. 19 | type Suite struct { 20 | *assert.Assertions 21 | require *require.Assertions 22 | t *testing.T 23 | } 24 | 25 | // T retrieves the current *testing.T context. 26 | func (suite *Suite) T() *testing.T { 27 | return suite.t 28 | } 29 | 30 | // SetT sets the current *testing.T context. 31 | func (suite *Suite) SetT(t *testing.T) { 32 | suite.t = t 33 | suite.Assertions = assert.New(t) 34 | suite.require = require.New(t) 35 | } 36 | 37 | // Require returns a require context for suite. 38 | func (suite *Suite) Require() *require.Assertions { 39 | if suite.require == nil { 40 | suite.require = require.New(suite.T()) 41 | } 42 | return suite.require 43 | } 44 | 45 | // Assert returns an assert context for suite. Normally, you can call 46 | // `suite.NoError(expected, actual)`, but for situations where the embedded 47 | // methods are overridden (for example, you might want to override 48 | // assert.Assertions with require.Assertions), this method is provided so you 49 | // can call `suite.Assert().NoError()`. 50 | func (suite *Suite) Assert() *assert.Assertions { 51 | if suite.Assertions == nil { 52 | suite.Assertions = assert.New(suite.T()) 53 | } 54 | return suite.Assertions 55 | } 56 | 57 | // Run takes a testing suite and runs all of the tests attached 58 | // to it. 59 | func Run(t *testing.T, suite TestingSuite) { 60 | suite.SetT(t) 61 | 62 | if setupAllSuite, ok := suite.(SetupAllSuite); ok { 63 | setupAllSuite.SetupSuite() 64 | } 65 | defer func() { 66 | if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok { 67 | tearDownAllSuite.TearDownSuite() 68 | } 69 | }() 70 | 71 | methodFinder := reflect.TypeOf(suite) 72 | tests := []testing.InternalTest{} 73 | for index := 0; index < methodFinder.NumMethod(); index++ { 74 | method := methodFinder.Method(index) 75 | ok, err := methodFilter(method.Name) 76 | if err != nil { 77 | fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err) 78 | os.Exit(1) 79 | } 80 | if ok { 81 | test := testing.InternalTest{ 82 | Name: method.Name, 83 | F: func(t *testing.T) { 84 | parentT := suite.T() 85 | suite.SetT(t) 86 | if setupTestSuite, ok := suite.(SetupTestSuite); ok { 87 | setupTestSuite.SetupTest() 88 | } 89 | if beforeTestSuite, ok := suite.(BeforeTest); ok { 90 | beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name) 91 | } 92 | defer func() { 93 | if afterTestSuite, ok := suite.(AfterTest); ok { 94 | afterTestSuite.AfterTest(methodFinder.Elem().Name(), method.Name) 95 | } 96 | if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok { 97 | tearDownTestSuite.TearDownTest() 98 | } 99 | suite.SetT(parentT) 100 | }() 101 | method.Func.Call([]reflect.Value{reflect.ValueOf(suite)}) 102 | }, 103 | } 104 | tests = append(tests, test) 105 | } 106 | } 107 | 108 | if !testing.RunTests(func(_, _ string) (bool, error) { return true, nil }, 109 | tests) { 110 | t.Fail() 111 | } 112 | } 113 | 114 | // Filtering method according to set regular expression 115 | // specified command-line argument -m 116 | func methodFilter(name string) (bool, error) { 117 | if ok, _ := regexp.MatchString("^Test", name); !ok { 118 | return false, nil 119 | } 120 | return regexp.MatchString(*matchMethod, name) 121 | } 122 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/bypass.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is not running on Google App Engine, compiled by GopherJS, and 17 | // "-tags safe" is not added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // +build !js,!appengine,!safe,!disableunsafe 20 | 21 | package spew 22 | 23 | import ( 24 | "reflect" 25 | "unsafe" 26 | ) 27 | 28 | const ( 29 | // UnsafeDisabled is a build-time constant which specifies whether or 30 | // not access to the unsafe package is available. 31 | UnsafeDisabled = false 32 | 33 | // ptrSize is the size of a pointer on the current arch. 34 | ptrSize = unsafe.Sizeof((*byte)(nil)) 35 | ) 36 | 37 | var ( 38 | // offsetPtr, offsetScalar, and offsetFlag are the offsets for the 39 | // internal reflect.Value fields. These values are valid before golang 40 | // commit ecccf07e7f9d which changed the format. The are also valid 41 | // after commit 82f48826c6c7 which changed the format again to mirror 42 | // the original format. Code in the init function updates these offsets 43 | // as necessary. 44 | offsetPtr = uintptr(ptrSize) 45 | offsetScalar = uintptr(0) 46 | offsetFlag = uintptr(ptrSize * 2) 47 | 48 | // flagKindWidth and flagKindShift indicate various bits that the 49 | // reflect package uses internally to track kind information. 50 | // 51 | // flagRO indicates whether or not the value field of a reflect.Value is 52 | // read-only. 53 | // 54 | // flagIndir indicates whether the value field of a reflect.Value is 55 | // the actual data or a pointer to the data. 56 | // 57 | // These values are valid before golang commit 90a7c3c86944 which 58 | // changed their positions. Code in the init function updates these 59 | // flags as necessary. 60 | flagKindWidth = uintptr(5) 61 | flagKindShift = uintptr(flagKindWidth - 1) 62 | flagRO = uintptr(1 << 0) 63 | flagIndir = uintptr(1 << 1) 64 | ) 65 | 66 | func init() { 67 | // Older versions of reflect.Value stored small integers directly in the 68 | // ptr field (which is named val in the older versions). Versions 69 | // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named 70 | // scalar for this purpose which unfortunately came before the flag 71 | // field, so the offset of the flag field is different for those 72 | // versions. 73 | // 74 | // This code constructs a new reflect.Value from a known small integer 75 | // and checks if the size of the reflect.Value struct indicates it has 76 | // the scalar field. When it does, the offsets are updated accordingly. 77 | vv := reflect.ValueOf(0xf00) 78 | if unsafe.Sizeof(vv) == (ptrSize * 4) { 79 | offsetScalar = ptrSize * 2 80 | offsetFlag = ptrSize * 3 81 | } 82 | 83 | // Commit 90a7c3c86944 changed the flag positions such that the low 84 | // order bits are the kind. This code extracts the kind from the flags 85 | // field and ensures it's the correct type. When it's not, the flag 86 | // order has been changed to the newer format, so the flags are updated 87 | // accordingly. 88 | upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) 89 | upfv := *(*uintptr)(upf) 90 | flagKindMask := uintptr((1<>flagKindShift != uintptr(reflect.Int) { 92 | flagKindShift = 0 93 | flagRO = 1 << 5 94 | flagIndir = 1 << 6 95 | 96 | // Commit adf9b30e5594 modified the flags to separate the 97 | // flagRO flag into two bits which specifies whether or not the 98 | // field is embedded. This causes flagIndir to move over a bit 99 | // and means that flagRO is the combination of either of the 100 | // original flagRO bit and the new bit. 101 | // 102 | // This code detects the change by extracting what used to be 103 | // the indirect bit to ensure it's set. When it's not, the flag 104 | // order has been changed to the newer format, so the flags are 105 | // updated accordingly. 106 | if upfv&flagIndir == 0 { 107 | flagRO = 3 << 5 108 | flagIndir = 1 << 7 109 | } 110 | } 111 | } 112 | 113 | // unsafeReflectValue converts the passed reflect.Value into a one that bypasses 114 | // the typical safety restrictions preventing access to unaddressable and 115 | // unexported data. It works by digging the raw pointer to the underlying 116 | // value out of the protected value and generating a new unprotected (unsafe) 117 | // reflect.Value to it. 118 | // 119 | // This allows us to check for implementations of the Stringer and error 120 | // interfaces to be used for pretty printing ordinarily unaddressable and 121 | // inaccessible values such as unexported struct fields. 122 | func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { 123 | indirects := 1 124 | vt := v.Type() 125 | upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) 126 | rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) 127 | if rvf&flagIndir != 0 { 128 | vt = reflect.PtrTo(v.Type()) 129 | indirects++ 130 | } else if offsetScalar != 0 { 131 | // The value is in the scalar field when it's not one of the 132 | // reference types. 133 | switch vt.Kind() { 134 | case reflect.Uintptr: 135 | case reflect.Chan: 136 | case reflect.Func: 137 | case reflect.Map: 138 | case reflect.Ptr: 139 | case reflect.UnsafePointer: 140 | default: 141 | upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + 142 | offsetScalar) 143 | } 144 | } 145 | 146 | pv := reflect.NewAt(vt, upv) 147 | rv = pv 148 | for i := 0; i < indirects; i++ { 149 | rv = rv.Elem() 150 | } 151 | return rv 152 | } 153 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is running on Google App Engine, compiled by GopherJS, or 17 | // "-tags safe" is added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // +build js appengine safe disableunsafe 20 | 21 | package spew 22 | 23 | import "reflect" 24 | 25 | const ( 26 | // UnsafeDisabled is a build-time constant which specifies whether or 27 | // not access to the unsafe package is available. 28 | UnsafeDisabled = true 29 | ) 30 | 31 | // unsafeReflectValue typically converts the passed reflect.Value into a one 32 | // that bypasses the typical safety restrictions preventing access to 33 | // unaddressable and unexported data. However, doing this relies on access to 34 | // the unsafe package. This is a stub version which simply returns the passed 35 | // reflect.Value when the unsafe package is not available. 36 | func unsafeReflectValue(v reflect.Value) reflect.Value { 37 | return v 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/common.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package spew 18 | 19 | import ( 20 | "bytes" 21 | "fmt" 22 | "io" 23 | "reflect" 24 | "sort" 25 | "strconv" 26 | ) 27 | 28 | // Some constants in the form of bytes to avoid string overhead. This mirrors 29 | // the technique used in the fmt package. 30 | var ( 31 | panicBytes = []byte("(PANIC=") 32 | plusBytes = []byte("+") 33 | iBytes = []byte("i") 34 | trueBytes = []byte("true") 35 | falseBytes = []byte("false") 36 | interfaceBytes = []byte("(interface {})") 37 | commaNewlineBytes = []byte(",\n") 38 | newlineBytes = []byte("\n") 39 | openBraceBytes = []byte("{") 40 | openBraceNewlineBytes = []byte("{\n") 41 | closeBraceBytes = []byte("}") 42 | asteriskBytes = []byte("*") 43 | colonBytes = []byte(":") 44 | colonSpaceBytes = []byte(": ") 45 | openParenBytes = []byte("(") 46 | closeParenBytes = []byte(")") 47 | spaceBytes = []byte(" ") 48 | pointerChainBytes = []byte("->") 49 | nilAngleBytes = []byte("") 50 | maxNewlineBytes = []byte("\n") 51 | maxShortBytes = []byte("") 52 | circularBytes = []byte("") 53 | circularShortBytes = []byte("") 54 | invalidAngleBytes = []byte("") 55 | openBracketBytes = []byte("[") 56 | closeBracketBytes = []byte("]") 57 | percentBytes = []byte("%") 58 | precisionBytes = []byte(".") 59 | openAngleBytes = []byte("<") 60 | closeAngleBytes = []byte(">") 61 | openMapBytes = []byte("map[") 62 | closeMapBytes = []byte("]") 63 | lenEqualsBytes = []byte("len=") 64 | capEqualsBytes = []byte("cap=") 65 | ) 66 | 67 | // hexDigits is used to map a decimal value to a hex digit. 68 | var hexDigits = "0123456789abcdef" 69 | 70 | // catchPanic handles any panics that might occur during the handleMethods 71 | // calls. 72 | func catchPanic(w io.Writer, v reflect.Value) { 73 | if err := recover(); err != nil { 74 | w.Write(panicBytes) 75 | fmt.Fprintf(w, "%v", err) 76 | w.Write(closeParenBytes) 77 | } 78 | } 79 | 80 | // handleMethods attempts to call the Error and String methods on the underlying 81 | // type the passed reflect.Value represents and outputes the result to Writer w. 82 | // 83 | // It handles panics in any called methods by catching and displaying the error 84 | // as the formatted value. 85 | func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) { 86 | // We need an interface to check if the type implements the error or 87 | // Stringer interface. However, the reflect package won't give us an 88 | // interface on certain things like unexported struct fields in order 89 | // to enforce visibility rules. We use unsafe, when it's available, 90 | // to bypass these restrictions since this package does not mutate the 91 | // values. 92 | if !v.CanInterface() { 93 | if UnsafeDisabled { 94 | return false 95 | } 96 | 97 | v = unsafeReflectValue(v) 98 | } 99 | 100 | // Choose whether or not to do error and Stringer interface lookups against 101 | // the base type or a pointer to the base type depending on settings. 102 | // Technically calling one of these methods with a pointer receiver can 103 | // mutate the value, however, types which choose to satisify an error or 104 | // Stringer interface with a pointer receiver should not be mutating their 105 | // state inside these interface methods. 106 | if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() { 107 | v = unsafeReflectValue(v) 108 | } 109 | if v.CanAddr() { 110 | v = v.Addr() 111 | } 112 | 113 | // Is it an error or Stringer? 114 | switch iface := v.Interface().(type) { 115 | case error: 116 | defer catchPanic(w, v) 117 | if cs.ContinueOnMethod { 118 | w.Write(openParenBytes) 119 | w.Write([]byte(iface.Error())) 120 | w.Write(closeParenBytes) 121 | w.Write(spaceBytes) 122 | return false 123 | } 124 | 125 | w.Write([]byte(iface.Error())) 126 | return true 127 | 128 | case fmt.Stringer: 129 | defer catchPanic(w, v) 130 | if cs.ContinueOnMethod { 131 | w.Write(openParenBytes) 132 | w.Write([]byte(iface.String())) 133 | w.Write(closeParenBytes) 134 | w.Write(spaceBytes) 135 | return false 136 | } 137 | w.Write([]byte(iface.String())) 138 | return true 139 | } 140 | return false 141 | } 142 | 143 | // printBool outputs a boolean value as true or false to Writer w. 144 | func printBool(w io.Writer, val bool) { 145 | if val { 146 | w.Write(trueBytes) 147 | } else { 148 | w.Write(falseBytes) 149 | } 150 | } 151 | 152 | // printInt outputs a signed integer value to Writer w. 153 | func printInt(w io.Writer, val int64, base int) { 154 | w.Write([]byte(strconv.FormatInt(val, base))) 155 | } 156 | 157 | // printUint outputs an unsigned integer value to Writer w. 158 | func printUint(w io.Writer, val uint64, base int) { 159 | w.Write([]byte(strconv.FormatUint(val, base))) 160 | } 161 | 162 | // printFloat outputs a floating point value using the specified precision, 163 | // which is expected to be 32 or 64bit, to Writer w. 164 | func printFloat(w io.Writer, val float64, precision int) { 165 | w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision))) 166 | } 167 | 168 | // printComplex outputs a complex value using the specified float precision 169 | // for the real and imaginary parts to Writer w. 170 | func printComplex(w io.Writer, c complex128, floatPrecision int) { 171 | r := real(c) 172 | w.Write(openParenBytes) 173 | w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision))) 174 | i := imag(c) 175 | if i >= 0 { 176 | w.Write(plusBytes) 177 | } 178 | w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) 179 | w.Write(iBytes) 180 | w.Write(closeParenBytes) 181 | } 182 | 183 | // printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x' 184 | // prefix to Writer w. 185 | func printHexPtr(w io.Writer, p uintptr) { 186 | // Null pointer. 187 | num := uint64(p) 188 | if num == 0 { 189 | w.Write(nilAngleBytes) 190 | return 191 | } 192 | 193 | // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix 194 | buf := make([]byte, 18) 195 | 196 | // It's simpler to construct the hex string right to left. 197 | base := uint64(16) 198 | i := len(buf) - 1 199 | for num >= base { 200 | buf[i] = hexDigits[num%base] 201 | num /= base 202 | i-- 203 | } 204 | buf[i] = hexDigits[num] 205 | 206 | // Add '0x' prefix. 207 | i-- 208 | buf[i] = 'x' 209 | i-- 210 | buf[i] = '0' 211 | 212 | // Strip unused leading bytes. 213 | buf = buf[i:] 214 | w.Write(buf) 215 | } 216 | 217 | // valuesSorter implements sort.Interface to allow a slice of reflect.Value 218 | // elements to be sorted. 219 | type valuesSorter struct { 220 | values []reflect.Value 221 | strings []string // either nil or same len and values 222 | cs *ConfigState 223 | } 224 | 225 | // newValuesSorter initializes a valuesSorter instance, which holds a set of 226 | // surrogate keys on which the data should be sorted. It uses flags in 227 | // ConfigState to decide if and how to populate those surrogate keys. 228 | func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface { 229 | vs := &valuesSorter{values: values, cs: cs} 230 | if canSortSimply(vs.values[0].Kind()) { 231 | return vs 232 | } 233 | if !cs.DisableMethods { 234 | vs.strings = make([]string, len(values)) 235 | for i := range vs.values { 236 | b := bytes.Buffer{} 237 | if !handleMethods(cs, &b, vs.values[i]) { 238 | vs.strings = nil 239 | break 240 | } 241 | vs.strings[i] = b.String() 242 | } 243 | } 244 | if vs.strings == nil && cs.SpewKeys { 245 | vs.strings = make([]string, len(values)) 246 | for i := range vs.values { 247 | vs.strings[i] = Sprintf("%#v", vs.values[i].Interface()) 248 | } 249 | } 250 | return vs 251 | } 252 | 253 | // canSortSimply tests whether a reflect.Kind is a primitive that can be sorted 254 | // directly, or whether it should be considered for sorting by surrogate keys 255 | // (if the ConfigState allows it). 256 | func canSortSimply(kind reflect.Kind) bool { 257 | // This switch parallels valueSortLess, except for the default case. 258 | switch kind { 259 | case reflect.Bool: 260 | return true 261 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: 262 | return true 263 | case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: 264 | return true 265 | case reflect.Float32, reflect.Float64: 266 | return true 267 | case reflect.String: 268 | return true 269 | case reflect.Uintptr: 270 | return true 271 | case reflect.Array: 272 | return true 273 | } 274 | return false 275 | } 276 | 277 | // Len returns the number of values in the slice. It is part of the 278 | // sort.Interface implementation. 279 | func (s *valuesSorter) Len() int { 280 | return len(s.values) 281 | } 282 | 283 | // Swap swaps the values at the passed indices. It is part of the 284 | // sort.Interface implementation. 285 | func (s *valuesSorter) Swap(i, j int) { 286 | s.values[i], s.values[j] = s.values[j], s.values[i] 287 | if s.strings != nil { 288 | s.strings[i], s.strings[j] = s.strings[j], s.strings[i] 289 | } 290 | } 291 | 292 | // valueSortLess returns whether the first value should sort before the second 293 | // value. It is used by valueSorter.Less as part of the sort.Interface 294 | // implementation. 295 | func valueSortLess(a, b reflect.Value) bool { 296 | switch a.Kind() { 297 | case reflect.Bool: 298 | return !a.Bool() && b.Bool() 299 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: 300 | return a.Int() < b.Int() 301 | case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: 302 | return a.Uint() < b.Uint() 303 | case reflect.Float32, reflect.Float64: 304 | return a.Float() < b.Float() 305 | case reflect.String: 306 | return a.String() < b.String() 307 | case reflect.Uintptr: 308 | return a.Uint() < b.Uint() 309 | case reflect.Array: 310 | // Compare the contents of both arrays. 311 | l := a.Len() 312 | for i := 0; i < l; i++ { 313 | av := a.Index(i) 314 | bv := b.Index(i) 315 | if av.Interface() == bv.Interface() { 316 | continue 317 | } 318 | return valueSortLess(av, bv) 319 | } 320 | } 321 | return a.String() < b.String() 322 | } 323 | 324 | // Less returns whether the value at index i should sort before the 325 | // value at index j. It is part of the sort.Interface implementation. 326 | func (s *valuesSorter) Less(i, j int) bool { 327 | if s.strings == nil { 328 | return valueSortLess(s.values[i], s.values[j]) 329 | } 330 | return s.strings[i] < s.strings[j] 331 | } 332 | 333 | // sortValues is a sort function that handles both native types and any type that 334 | // can be converted to error or Stringer. Other inputs are sorted according to 335 | // their Value.String() value to ensure display stability. 336 | func sortValues(values []reflect.Value, cs *ConfigState) { 337 | if len(values) == 0 { 338 | return 339 | } 340 | sort.Sort(newValuesSorter(values, cs)) 341 | } 342 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package spew 18 | 19 | import ( 20 | "bytes" 21 | "fmt" 22 | "io" 23 | "os" 24 | ) 25 | 26 | // ConfigState houses the configuration options used by spew to format and 27 | // display values. There is a global instance, Config, that is used to control 28 | // all top-level Formatter and Dump functionality. Each ConfigState instance 29 | // provides methods equivalent to the top-level functions. 30 | // 31 | // The zero value for ConfigState provides no indentation. You would typically 32 | // want to set it to a space or a tab. 33 | // 34 | // Alternatively, you can use NewDefaultConfig to get a ConfigState instance 35 | // with default settings. See the documentation of NewDefaultConfig for default 36 | // values. 37 | type ConfigState struct { 38 | // Indent specifies the string to use for each indentation level. The 39 | // global config instance that all top-level functions use set this to a 40 | // single space by default. If you would like more indentation, you might 41 | // set this to a tab with "\t" or perhaps two spaces with " ". 42 | Indent string 43 | 44 | // MaxDepth controls the maximum number of levels to descend into nested 45 | // data structures. The default, 0, means there is no limit. 46 | // 47 | // NOTE: Circular data structures are properly detected, so it is not 48 | // necessary to set this value unless you specifically want to limit deeply 49 | // nested data structures. 50 | MaxDepth int 51 | 52 | // DisableMethods specifies whether or not error and Stringer interfaces are 53 | // invoked for types that implement them. 54 | DisableMethods bool 55 | 56 | // DisablePointerMethods specifies whether or not to check for and invoke 57 | // error and Stringer interfaces on types which only accept a pointer 58 | // receiver when the current type is not a pointer. 59 | // 60 | // NOTE: This might be an unsafe action since calling one of these methods 61 | // with a pointer receiver could technically mutate the value, however, 62 | // in practice, types which choose to satisify an error or Stringer 63 | // interface with a pointer receiver should not be mutating their state 64 | // inside these interface methods. As a result, this option relies on 65 | // access to the unsafe package, so it will not have any effect when 66 | // running in environments without access to the unsafe package such as 67 | // Google App Engine or with the "safe" build tag specified. 68 | DisablePointerMethods bool 69 | 70 | // DisablePointerAddresses specifies whether to disable the printing of 71 | // pointer addresses. This is useful when diffing data structures in tests. 72 | DisablePointerAddresses bool 73 | 74 | // DisableCapacities specifies whether to disable the printing of capacities 75 | // for arrays, slices, maps and channels. This is useful when diffing 76 | // data structures in tests. 77 | DisableCapacities bool 78 | 79 | // ContinueOnMethod specifies whether or not recursion should continue once 80 | // a custom error or Stringer interface is invoked. The default, false, 81 | // means it will print the results of invoking the custom error or Stringer 82 | // interface and return immediately instead of continuing to recurse into 83 | // the internals of the data type. 84 | // 85 | // NOTE: This flag does not have any effect if method invocation is disabled 86 | // via the DisableMethods or DisablePointerMethods options. 87 | ContinueOnMethod bool 88 | 89 | // SortKeys specifies map keys should be sorted before being printed. Use 90 | // this to have a more deterministic, diffable output. Note that only 91 | // native types (bool, int, uint, floats, uintptr and string) and types 92 | // that support the error or Stringer interfaces (if methods are 93 | // enabled) are supported, with other types sorted according to the 94 | // reflect.Value.String() output which guarantees display stability. 95 | SortKeys bool 96 | 97 | // SpewKeys specifies that, as a last resort attempt, map keys should 98 | // be spewed to strings and sorted by those strings. This is only 99 | // considered if SortKeys is true. 100 | SpewKeys bool 101 | } 102 | 103 | // Config is the active configuration of the top-level functions. 104 | // The configuration can be changed by modifying the contents of spew.Config. 105 | var Config = ConfigState{Indent: " "} 106 | 107 | // Errorf is a wrapper for fmt.Errorf that treats each argument as if it were 108 | // passed with a Formatter interface returned by c.NewFormatter. It returns 109 | // the formatted string as a value that satisfies error. See NewFormatter 110 | // for formatting details. 111 | // 112 | // This function is shorthand for the following syntax: 113 | // 114 | // fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b)) 115 | func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) { 116 | return fmt.Errorf(format, c.convertArgs(a)...) 117 | } 118 | 119 | // Fprint is a wrapper for fmt.Fprint that treats each argument as if it were 120 | // passed with a Formatter interface returned by c.NewFormatter. It returns 121 | // the number of bytes written and any write error encountered. See 122 | // NewFormatter for formatting details. 123 | // 124 | // This function is shorthand for the following syntax: 125 | // 126 | // fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b)) 127 | func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) { 128 | return fmt.Fprint(w, c.convertArgs(a)...) 129 | } 130 | 131 | // Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were 132 | // passed with a Formatter interface returned by c.NewFormatter. It returns 133 | // the number of bytes written and any write error encountered. See 134 | // NewFormatter for formatting details. 135 | // 136 | // This function is shorthand for the following syntax: 137 | // 138 | // fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b)) 139 | func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { 140 | return fmt.Fprintf(w, format, c.convertArgs(a)...) 141 | } 142 | 143 | // Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it 144 | // passed with a Formatter interface returned by c.NewFormatter. See 145 | // NewFormatter for formatting details. 146 | // 147 | // This function is shorthand for the following syntax: 148 | // 149 | // fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b)) 150 | func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { 151 | return fmt.Fprintln(w, c.convertArgs(a)...) 152 | } 153 | 154 | // Print is a wrapper for fmt.Print that treats each argument as if it were 155 | // passed with a Formatter interface returned by c.NewFormatter. It returns 156 | // the number of bytes written and any write error encountered. See 157 | // NewFormatter for formatting details. 158 | // 159 | // This function is shorthand for the following syntax: 160 | // 161 | // fmt.Print(c.NewFormatter(a), c.NewFormatter(b)) 162 | func (c *ConfigState) Print(a ...interface{}) (n int, err error) { 163 | return fmt.Print(c.convertArgs(a)...) 164 | } 165 | 166 | // Printf is a wrapper for fmt.Printf that treats each argument as if it were 167 | // passed with a Formatter interface returned by c.NewFormatter. It returns 168 | // the number of bytes written and any write error encountered. See 169 | // NewFormatter for formatting details. 170 | // 171 | // This function is shorthand for the following syntax: 172 | // 173 | // fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b)) 174 | func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) { 175 | return fmt.Printf(format, c.convertArgs(a)...) 176 | } 177 | 178 | // Println is a wrapper for fmt.Println that treats each argument as if it were 179 | // passed with a Formatter interface returned by c.NewFormatter. It returns 180 | // the number of bytes written and any write error encountered. See 181 | // NewFormatter for formatting details. 182 | // 183 | // This function is shorthand for the following syntax: 184 | // 185 | // fmt.Println(c.NewFormatter(a), c.NewFormatter(b)) 186 | func (c *ConfigState) Println(a ...interface{}) (n int, err error) { 187 | return fmt.Println(c.convertArgs(a)...) 188 | } 189 | 190 | // Sprint is a wrapper for fmt.Sprint that treats each argument as if it were 191 | // passed with a Formatter interface returned by c.NewFormatter. It returns 192 | // the resulting string. See NewFormatter for formatting details. 193 | // 194 | // This function is shorthand for the following syntax: 195 | // 196 | // fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b)) 197 | func (c *ConfigState) Sprint(a ...interface{}) string { 198 | return fmt.Sprint(c.convertArgs(a)...) 199 | } 200 | 201 | // Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were 202 | // passed with a Formatter interface returned by c.NewFormatter. It returns 203 | // the resulting string. See NewFormatter for formatting details. 204 | // 205 | // This function is shorthand for the following syntax: 206 | // 207 | // fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b)) 208 | func (c *ConfigState) Sprintf(format string, a ...interface{}) string { 209 | return fmt.Sprintf(format, c.convertArgs(a)...) 210 | } 211 | 212 | // Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it 213 | // were passed with a Formatter interface returned by c.NewFormatter. It 214 | // returns the resulting string. See NewFormatter for formatting details. 215 | // 216 | // This function is shorthand for the following syntax: 217 | // 218 | // fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b)) 219 | func (c *ConfigState) Sprintln(a ...interface{}) string { 220 | return fmt.Sprintln(c.convertArgs(a)...) 221 | } 222 | 223 | /* 224 | NewFormatter returns a custom formatter that satisfies the fmt.Formatter 225 | interface. As a result, it integrates cleanly with standard fmt package 226 | printing functions. The formatter is useful for inline printing of smaller data 227 | types similar to the standard %v format specifier. 228 | 229 | The custom formatter only responds to the %v (most compact), %+v (adds pointer 230 | addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb 231 | combinations. Any other verbs such as %x and %q will be sent to the the 232 | standard fmt package for formatting. In addition, the custom formatter ignores 233 | the width and precision arguments (however they will still work on the format 234 | specifiers not handled by the custom formatter). 235 | 236 | Typically this function shouldn't be called directly. It is much easier to make 237 | use of the custom formatter by calling one of the convenience functions such as 238 | c.Printf, c.Println, or c.Printf. 239 | */ 240 | func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter { 241 | return newFormatter(c, v) 242 | } 243 | 244 | // Fdump formats and displays the passed arguments to io.Writer w. It formats 245 | // exactly the same as Dump. 246 | func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) { 247 | fdump(c, w, a...) 248 | } 249 | 250 | /* 251 | Dump displays the passed parameters to standard out with newlines, customizable 252 | indentation, and additional debug information such as complete types and all 253 | pointer addresses used to indirect to the final value. It provides the 254 | following features over the built-in printing facilities provided by the fmt 255 | package: 256 | 257 | * Pointers are dereferenced and followed 258 | * Circular data structures are detected and handled properly 259 | * Custom Stringer/error interfaces are optionally invoked, including 260 | on unexported types 261 | * Custom types which only implement the Stringer/error interfaces via 262 | a pointer receiver are optionally invoked when passing non-pointer 263 | variables 264 | * Byte arrays and slices are dumped like the hexdump -C command which 265 | includes offsets, byte values in hex, and ASCII output 266 | 267 | The configuration options are controlled by modifying the public members 268 | of c. See ConfigState for options documentation. 269 | 270 | See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to 271 | get the formatted result as a string. 272 | */ 273 | func (c *ConfigState) Dump(a ...interface{}) { 274 | fdump(c, os.Stdout, a...) 275 | } 276 | 277 | // Sdump returns a string with the passed arguments formatted exactly the same 278 | // as Dump. 279 | func (c *ConfigState) Sdump(a ...interface{}) string { 280 | var buf bytes.Buffer 281 | fdump(c, &buf, a...) 282 | return buf.String() 283 | } 284 | 285 | // convertArgs accepts a slice of arguments and returns a slice of the same 286 | // length with each argument converted to a spew Formatter interface using 287 | // the ConfigState associated with s. 288 | func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) { 289 | formatters = make([]interface{}, len(args)) 290 | for index, arg := range args { 291 | formatters[index] = newFormatter(c, arg) 292 | } 293 | return formatters 294 | } 295 | 296 | // NewDefaultConfig returns a ConfigState with the following default settings. 297 | // 298 | // Indent: " " 299 | // MaxDepth: 0 300 | // DisableMethods: false 301 | // DisablePointerMethods: false 302 | // ContinueOnMethod: false 303 | // SortKeys: false 304 | func NewDefaultConfig() *ConfigState { 305 | return &ConfigState{Indent: " "} 306 | } 307 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* 18 | Package spew implements a deep pretty printer for Go data structures to aid in 19 | debugging. 20 | 21 | A quick overview of the additional features spew provides over the built-in 22 | printing facilities for Go data types are as follows: 23 | 24 | * Pointers are dereferenced and followed 25 | * Circular data structures are detected and handled properly 26 | * Custom Stringer/error interfaces are optionally invoked, including 27 | on unexported types 28 | * Custom types which only implement the Stringer/error interfaces via 29 | a pointer receiver are optionally invoked when passing non-pointer 30 | variables 31 | * Byte arrays and slices are dumped like the hexdump -C command which 32 | includes offsets, byte values in hex, and ASCII output (only when using 33 | Dump style) 34 | 35 | There are two different approaches spew allows for dumping Go data structures: 36 | 37 | * Dump style which prints with newlines, customizable indentation, 38 | and additional debug information such as types and all pointer addresses 39 | used to indirect to the final value 40 | * A custom Formatter interface that integrates cleanly with the standard fmt 41 | package and replaces %v, %+v, %#v, and %#+v to provide inline printing 42 | similar to the default %v while providing the additional functionality 43 | outlined above and passing unsupported format verbs such as %x and %q 44 | along to fmt 45 | 46 | Quick Start 47 | 48 | This section demonstrates how to quickly get started with spew. See the 49 | sections below for further details on formatting and configuration options. 50 | 51 | To dump a variable with full newlines, indentation, type, and pointer 52 | information use Dump, Fdump, or Sdump: 53 | spew.Dump(myVar1, myVar2, ...) 54 | spew.Fdump(someWriter, myVar1, myVar2, ...) 55 | str := spew.Sdump(myVar1, myVar2, ...) 56 | 57 | Alternatively, if you would prefer to use format strings with a compacted inline 58 | printing style, use the convenience wrappers Printf, Fprintf, etc with 59 | %v (most compact), %+v (adds pointer addresses), %#v (adds types), or 60 | %#+v (adds types and pointer addresses): 61 | spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) 62 | spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 63 | spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) 64 | spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 65 | 66 | Configuration Options 67 | 68 | Configuration of spew is handled by fields in the ConfigState type. For 69 | convenience, all of the top-level functions use a global state available 70 | via the spew.Config global. 71 | 72 | It is also possible to create a ConfigState instance that provides methods 73 | equivalent to the top-level functions. This allows concurrent configuration 74 | options. See the ConfigState documentation for more details. 75 | 76 | The following configuration options are available: 77 | * Indent 78 | String to use for each indentation level for Dump functions. 79 | It is a single space by default. A popular alternative is "\t". 80 | 81 | * MaxDepth 82 | Maximum number of levels to descend into nested data structures. 83 | There is no limit by default. 84 | 85 | * DisableMethods 86 | Disables invocation of error and Stringer interface methods. 87 | Method invocation is enabled by default. 88 | 89 | * DisablePointerMethods 90 | Disables invocation of error and Stringer interface methods on types 91 | which only accept pointer receivers from non-pointer variables. 92 | Pointer method invocation is enabled by default. 93 | 94 | * ContinueOnMethod 95 | Enables recursion into types after invoking error and Stringer interface 96 | methods. Recursion after method invocation is disabled by default. 97 | 98 | * SortKeys 99 | Specifies map keys should be sorted before being printed. Use 100 | this to have a more deterministic, diffable output. Note that 101 | only native types (bool, int, uint, floats, uintptr and string) 102 | and types which implement error or Stringer interfaces are 103 | supported with other types sorted according to the 104 | reflect.Value.String() output which guarantees display 105 | stability. Natural map order is used by default. 106 | 107 | * SpewKeys 108 | Specifies that, as a last resort attempt, map keys should be 109 | spewed to strings and sorted by those strings. This is only 110 | considered if SortKeys is true. 111 | 112 | Dump Usage 113 | 114 | Simply call spew.Dump with a list of variables you want to dump: 115 | 116 | spew.Dump(myVar1, myVar2, ...) 117 | 118 | You may also call spew.Fdump if you would prefer to output to an arbitrary 119 | io.Writer. For example, to dump to standard error: 120 | 121 | spew.Fdump(os.Stderr, myVar1, myVar2, ...) 122 | 123 | A third option is to call spew.Sdump to get the formatted output as a string: 124 | 125 | str := spew.Sdump(myVar1, myVar2, ...) 126 | 127 | Sample Dump Output 128 | 129 | See the Dump example for details on the setup of the types and variables being 130 | shown here. 131 | 132 | (main.Foo) { 133 | unexportedField: (*main.Bar)(0xf84002e210)({ 134 | flag: (main.Flag) flagTwo, 135 | data: (uintptr) 136 | }), 137 | ExportedField: (map[interface {}]interface {}) (len=1) { 138 | (string) (len=3) "one": (bool) true 139 | } 140 | } 141 | 142 | Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C 143 | command as shown. 144 | ([]uint8) (len=32 cap=32) { 145 | 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | 146 | 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| 147 | 00000020 31 32 |12| 148 | } 149 | 150 | Custom Formatter 151 | 152 | Spew provides a custom formatter that implements the fmt.Formatter interface 153 | so that it integrates cleanly with standard fmt package printing functions. The 154 | formatter is useful for inline printing of smaller data types similar to the 155 | standard %v format specifier. 156 | 157 | The custom formatter only responds to the %v (most compact), %+v (adds pointer 158 | addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb 159 | combinations. Any other verbs such as %x and %q will be sent to the the 160 | standard fmt package for formatting. In addition, the custom formatter ignores 161 | the width and precision arguments (however they will still work on the format 162 | specifiers not handled by the custom formatter). 163 | 164 | Custom Formatter Usage 165 | 166 | The simplest way to make use of the spew custom formatter is to call one of the 167 | convenience functions such as spew.Printf, spew.Println, or spew.Printf. The 168 | functions have syntax you are most likely already familiar with: 169 | 170 | spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) 171 | spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 172 | spew.Println(myVar, myVar2) 173 | spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) 174 | spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 175 | 176 | See the Index for the full list convenience functions. 177 | 178 | Sample Formatter Output 179 | 180 | Double pointer to a uint8: 181 | %v: <**>5 182 | %+v: <**>(0xf8400420d0->0xf8400420c8)5 183 | %#v: (**uint8)5 184 | %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 185 | 186 | Pointer to circular struct with a uint8 field and a pointer to itself: 187 | %v: <*>{1 <*>} 188 | %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} 189 | %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} 190 | %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)} 191 | 192 | See the Printf example for details on the setup of variables being shown 193 | here. 194 | 195 | Errors 196 | 197 | Since it is possible for custom Stringer/error interfaces to panic, spew 198 | detects them and handles them internally by printing the panic information 199 | inline with the output. Since spew is intended to provide deep pretty printing 200 | capabilities on structures, it intentionally does not return any errors. 201 | */ 202 | package spew 203 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/format.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package spew 18 | 19 | import ( 20 | "bytes" 21 | "fmt" 22 | "reflect" 23 | "strconv" 24 | "strings" 25 | ) 26 | 27 | // supportedFlags is a list of all the character flags supported by fmt package. 28 | const supportedFlags = "0-+# " 29 | 30 | // formatState implements the fmt.Formatter interface and contains information 31 | // about the state of a formatting operation. The NewFormatter function can 32 | // be used to get a new Formatter which can be used directly as arguments 33 | // in standard fmt package printing calls. 34 | type formatState struct { 35 | value interface{} 36 | fs fmt.State 37 | depth int 38 | pointers map[uintptr]int 39 | ignoreNextType bool 40 | cs *ConfigState 41 | } 42 | 43 | // buildDefaultFormat recreates the original format string without precision 44 | // and width information to pass in to fmt.Sprintf in the case of an 45 | // unrecognized type. Unless new types are added to the language, this 46 | // function won't ever be called. 47 | func (f *formatState) buildDefaultFormat() (format string) { 48 | buf := bytes.NewBuffer(percentBytes) 49 | 50 | for _, flag := range supportedFlags { 51 | if f.fs.Flag(int(flag)) { 52 | buf.WriteRune(flag) 53 | } 54 | } 55 | 56 | buf.WriteRune('v') 57 | 58 | format = buf.String() 59 | return format 60 | } 61 | 62 | // constructOrigFormat recreates the original format string including precision 63 | // and width information to pass along to the standard fmt package. This allows 64 | // automatic deferral of all format strings this package doesn't support. 65 | func (f *formatState) constructOrigFormat(verb rune) (format string) { 66 | buf := bytes.NewBuffer(percentBytes) 67 | 68 | for _, flag := range supportedFlags { 69 | if f.fs.Flag(int(flag)) { 70 | buf.WriteRune(flag) 71 | } 72 | } 73 | 74 | if width, ok := f.fs.Width(); ok { 75 | buf.WriteString(strconv.Itoa(width)) 76 | } 77 | 78 | if precision, ok := f.fs.Precision(); ok { 79 | buf.Write(precisionBytes) 80 | buf.WriteString(strconv.Itoa(precision)) 81 | } 82 | 83 | buf.WriteRune(verb) 84 | 85 | format = buf.String() 86 | return format 87 | } 88 | 89 | // unpackValue returns values inside of non-nil interfaces when possible and 90 | // ensures that types for values which have been unpacked from an interface 91 | // are displayed when the show types flag is also set. 92 | // This is useful for data types like structs, arrays, slices, and maps which 93 | // can contain varying types packed inside an interface. 94 | func (f *formatState) unpackValue(v reflect.Value) reflect.Value { 95 | if v.Kind() == reflect.Interface { 96 | f.ignoreNextType = false 97 | if !v.IsNil() { 98 | v = v.Elem() 99 | } 100 | } 101 | return v 102 | } 103 | 104 | // formatPtr handles formatting of pointers by indirecting them as necessary. 105 | func (f *formatState) formatPtr(v reflect.Value) { 106 | // Display nil if top level pointer is nil. 107 | showTypes := f.fs.Flag('#') 108 | if v.IsNil() && (!showTypes || f.ignoreNextType) { 109 | f.fs.Write(nilAngleBytes) 110 | return 111 | } 112 | 113 | // Remove pointers at or below the current depth from map used to detect 114 | // circular refs. 115 | for k, depth := range f.pointers { 116 | if depth >= f.depth { 117 | delete(f.pointers, k) 118 | } 119 | } 120 | 121 | // Keep list of all dereferenced pointers to possibly show later. 122 | pointerChain := make([]uintptr, 0) 123 | 124 | // Figure out how many levels of indirection there are by derferencing 125 | // pointers and unpacking interfaces down the chain while detecting circular 126 | // references. 127 | nilFound := false 128 | cycleFound := false 129 | indirects := 0 130 | ve := v 131 | for ve.Kind() == reflect.Ptr { 132 | if ve.IsNil() { 133 | nilFound = true 134 | break 135 | } 136 | indirects++ 137 | addr := ve.Pointer() 138 | pointerChain = append(pointerChain, addr) 139 | if pd, ok := f.pointers[addr]; ok && pd < f.depth { 140 | cycleFound = true 141 | indirects-- 142 | break 143 | } 144 | f.pointers[addr] = f.depth 145 | 146 | ve = ve.Elem() 147 | if ve.Kind() == reflect.Interface { 148 | if ve.IsNil() { 149 | nilFound = true 150 | break 151 | } 152 | ve = ve.Elem() 153 | } 154 | } 155 | 156 | // Display type or indirection level depending on flags. 157 | if showTypes && !f.ignoreNextType { 158 | f.fs.Write(openParenBytes) 159 | f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) 160 | f.fs.Write([]byte(ve.Type().String())) 161 | f.fs.Write(closeParenBytes) 162 | } else { 163 | if nilFound || cycleFound { 164 | indirects += strings.Count(ve.Type().String(), "*") 165 | } 166 | f.fs.Write(openAngleBytes) 167 | f.fs.Write([]byte(strings.Repeat("*", indirects))) 168 | f.fs.Write(closeAngleBytes) 169 | } 170 | 171 | // Display pointer information depending on flags. 172 | if f.fs.Flag('+') && (len(pointerChain) > 0) { 173 | f.fs.Write(openParenBytes) 174 | for i, addr := range pointerChain { 175 | if i > 0 { 176 | f.fs.Write(pointerChainBytes) 177 | } 178 | printHexPtr(f.fs, addr) 179 | } 180 | f.fs.Write(closeParenBytes) 181 | } 182 | 183 | // Display dereferenced value. 184 | switch { 185 | case nilFound == true: 186 | f.fs.Write(nilAngleBytes) 187 | 188 | case cycleFound == true: 189 | f.fs.Write(circularShortBytes) 190 | 191 | default: 192 | f.ignoreNextType = true 193 | f.format(ve) 194 | } 195 | } 196 | 197 | // format is the main workhorse for providing the Formatter interface. It 198 | // uses the passed reflect value to figure out what kind of object we are 199 | // dealing with and formats it appropriately. It is a recursive function, 200 | // however circular data structures are detected and handled properly. 201 | func (f *formatState) format(v reflect.Value) { 202 | // Handle invalid reflect values immediately. 203 | kind := v.Kind() 204 | if kind == reflect.Invalid { 205 | f.fs.Write(invalidAngleBytes) 206 | return 207 | } 208 | 209 | // Handle pointers specially. 210 | if kind == reflect.Ptr { 211 | f.formatPtr(v) 212 | return 213 | } 214 | 215 | // Print type information unless already handled elsewhere. 216 | if !f.ignoreNextType && f.fs.Flag('#') { 217 | f.fs.Write(openParenBytes) 218 | f.fs.Write([]byte(v.Type().String())) 219 | f.fs.Write(closeParenBytes) 220 | } 221 | f.ignoreNextType = false 222 | 223 | // Call Stringer/error interfaces if they exist and the handle methods 224 | // flag is enabled. 225 | if !f.cs.DisableMethods { 226 | if (kind != reflect.Invalid) && (kind != reflect.Interface) { 227 | if handled := handleMethods(f.cs, f.fs, v); handled { 228 | return 229 | } 230 | } 231 | } 232 | 233 | switch kind { 234 | case reflect.Invalid: 235 | // Do nothing. We should never get here since invalid has already 236 | // been handled above. 237 | 238 | case reflect.Bool: 239 | printBool(f.fs, v.Bool()) 240 | 241 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: 242 | printInt(f.fs, v.Int(), 10) 243 | 244 | case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: 245 | printUint(f.fs, v.Uint(), 10) 246 | 247 | case reflect.Float32: 248 | printFloat(f.fs, v.Float(), 32) 249 | 250 | case reflect.Float64: 251 | printFloat(f.fs, v.Float(), 64) 252 | 253 | case reflect.Complex64: 254 | printComplex(f.fs, v.Complex(), 32) 255 | 256 | case reflect.Complex128: 257 | printComplex(f.fs, v.Complex(), 64) 258 | 259 | case reflect.Slice: 260 | if v.IsNil() { 261 | f.fs.Write(nilAngleBytes) 262 | break 263 | } 264 | fallthrough 265 | 266 | case reflect.Array: 267 | f.fs.Write(openBracketBytes) 268 | f.depth++ 269 | if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { 270 | f.fs.Write(maxShortBytes) 271 | } else { 272 | numEntries := v.Len() 273 | for i := 0; i < numEntries; i++ { 274 | if i > 0 { 275 | f.fs.Write(spaceBytes) 276 | } 277 | f.ignoreNextType = true 278 | f.format(f.unpackValue(v.Index(i))) 279 | } 280 | } 281 | f.depth-- 282 | f.fs.Write(closeBracketBytes) 283 | 284 | case reflect.String: 285 | f.fs.Write([]byte(v.String())) 286 | 287 | case reflect.Interface: 288 | // The only time we should get here is for nil interfaces due to 289 | // unpackValue calls. 290 | if v.IsNil() { 291 | f.fs.Write(nilAngleBytes) 292 | } 293 | 294 | case reflect.Ptr: 295 | // Do nothing. We should never get here since pointers have already 296 | // been handled above. 297 | 298 | case reflect.Map: 299 | // nil maps should be indicated as different than empty maps 300 | if v.IsNil() { 301 | f.fs.Write(nilAngleBytes) 302 | break 303 | } 304 | 305 | f.fs.Write(openMapBytes) 306 | f.depth++ 307 | if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { 308 | f.fs.Write(maxShortBytes) 309 | } else { 310 | keys := v.MapKeys() 311 | if f.cs.SortKeys { 312 | sortValues(keys, f.cs) 313 | } 314 | for i, key := range keys { 315 | if i > 0 { 316 | f.fs.Write(spaceBytes) 317 | } 318 | f.ignoreNextType = true 319 | f.format(f.unpackValue(key)) 320 | f.fs.Write(colonBytes) 321 | f.ignoreNextType = true 322 | f.format(f.unpackValue(v.MapIndex(key))) 323 | } 324 | } 325 | f.depth-- 326 | f.fs.Write(closeMapBytes) 327 | 328 | case reflect.Struct: 329 | numFields := v.NumField() 330 | f.fs.Write(openBraceBytes) 331 | f.depth++ 332 | if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { 333 | f.fs.Write(maxShortBytes) 334 | } else { 335 | vt := v.Type() 336 | for i := 0; i < numFields; i++ { 337 | if i > 0 { 338 | f.fs.Write(spaceBytes) 339 | } 340 | vtf := vt.Field(i) 341 | if f.fs.Flag('+') || f.fs.Flag('#') { 342 | f.fs.Write([]byte(vtf.Name)) 343 | f.fs.Write(colonBytes) 344 | } 345 | f.format(f.unpackValue(v.Field(i))) 346 | } 347 | } 348 | f.depth-- 349 | f.fs.Write(closeBraceBytes) 350 | 351 | case reflect.Uintptr: 352 | printHexPtr(f.fs, uintptr(v.Uint())) 353 | 354 | case reflect.UnsafePointer, reflect.Chan, reflect.Func: 355 | printHexPtr(f.fs, v.Pointer()) 356 | 357 | // There were not any other types at the time this code was written, but 358 | // fall back to letting the default fmt package handle it if any get added. 359 | default: 360 | format := f.buildDefaultFormat() 361 | if v.CanInterface() { 362 | fmt.Fprintf(f.fs, format, v.Interface()) 363 | } else { 364 | fmt.Fprintf(f.fs, format, v.String()) 365 | } 366 | } 367 | } 368 | 369 | // Format satisfies the fmt.Formatter interface. See NewFormatter for usage 370 | // details. 371 | func (f *formatState) Format(fs fmt.State, verb rune) { 372 | f.fs = fs 373 | 374 | // Use standard formatting for verbs that are not v. 375 | if verb != 'v' { 376 | format := f.constructOrigFormat(verb) 377 | fmt.Fprintf(fs, format, f.value) 378 | return 379 | } 380 | 381 | if f.value == nil { 382 | if fs.Flag('#') { 383 | fs.Write(interfaceBytes) 384 | } 385 | fs.Write(nilAngleBytes) 386 | return 387 | } 388 | 389 | f.format(reflect.ValueOf(f.value)) 390 | } 391 | 392 | // newFormatter is a helper function to consolidate the logic from the various 393 | // public methods which take varying config states. 394 | func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter { 395 | fs := &formatState{value: v, cs: cs} 396 | fs.pointers = make(map[uintptr]int) 397 | return fs 398 | } 399 | 400 | /* 401 | NewFormatter returns a custom formatter that satisfies the fmt.Formatter 402 | interface. As a result, it integrates cleanly with standard fmt package 403 | printing functions. The formatter is useful for inline printing of smaller data 404 | types similar to the standard %v format specifier. 405 | 406 | The custom formatter only responds to the %v (most compact), %+v (adds pointer 407 | addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb 408 | combinations. Any other verbs such as %x and %q will be sent to the the 409 | standard fmt package for formatting. In addition, the custom formatter ignores 410 | the width and precision arguments (however they will still work on the format 411 | specifiers not handled by the custom formatter). 412 | 413 | Typically this function shouldn't be called directly. It is much easier to make 414 | use of the custom formatter by calling one of the convenience functions such as 415 | Printf, Println, or Fprintf. 416 | */ 417 | func NewFormatter(v interface{}) fmt.Formatter { 418 | return newFormatter(&Config, v) 419 | } 420 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew/spew.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package spew 18 | 19 | import ( 20 | "fmt" 21 | "io" 22 | ) 23 | 24 | // Errorf is a wrapper for fmt.Errorf that treats each argument as if it were 25 | // passed with a default Formatter interface returned by NewFormatter. It 26 | // returns the formatted string as a value that satisfies error. See 27 | // NewFormatter for formatting details. 28 | // 29 | // This function is shorthand for the following syntax: 30 | // 31 | // fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 32 | func Errorf(format string, a ...interface{}) (err error) { 33 | return fmt.Errorf(format, convertArgs(a)...) 34 | } 35 | 36 | // Fprint is a wrapper for fmt.Fprint that treats each argument as if it were 37 | // passed with a default Formatter interface returned by NewFormatter. It 38 | // returns the number of bytes written and any write error encountered. See 39 | // NewFormatter for formatting details. 40 | // 41 | // This function is shorthand for the following syntax: 42 | // 43 | // fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) 44 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) { 45 | return fmt.Fprint(w, convertArgs(a)...) 46 | } 47 | 48 | // Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were 49 | // passed with a default Formatter interface returned by NewFormatter. It 50 | // returns the number of bytes written and any write error encountered. See 51 | // NewFormatter for formatting details. 52 | // 53 | // This function is shorthand for the following syntax: 54 | // 55 | // fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) 56 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { 57 | return fmt.Fprintf(w, format, convertArgs(a)...) 58 | } 59 | 60 | // Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it 61 | // passed with a default Formatter interface returned by NewFormatter. See 62 | // NewFormatter for formatting details. 63 | // 64 | // This function is shorthand for the following syntax: 65 | // 66 | // fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) 67 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { 68 | return fmt.Fprintln(w, convertArgs(a)...) 69 | } 70 | 71 | // Print is a wrapper for fmt.Print that treats each argument as if it were 72 | // passed with a default Formatter interface returned by NewFormatter. It 73 | // returns the number of bytes written and any write error encountered. See 74 | // NewFormatter for formatting details. 75 | // 76 | // This function is shorthand for the following syntax: 77 | // 78 | // fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) 79 | func Print(a ...interface{}) (n int, err error) { 80 | return fmt.Print(convertArgs(a)...) 81 | } 82 | 83 | // Printf is a wrapper for fmt.Printf that treats each argument as if it were 84 | // passed with a default Formatter interface returned by NewFormatter. It 85 | // returns the number of bytes written and any write error encountered. See 86 | // NewFormatter for formatting details. 87 | // 88 | // This function is shorthand for the following syntax: 89 | // 90 | // fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 91 | func Printf(format string, a ...interface{}) (n int, err error) { 92 | return fmt.Printf(format, convertArgs(a)...) 93 | } 94 | 95 | // Println is a wrapper for fmt.Println that treats each argument as if it were 96 | // passed with a default Formatter interface returned by NewFormatter. It 97 | // returns the number of bytes written and any write error encountered. See 98 | // NewFormatter for formatting details. 99 | // 100 | // This function is shorthand for the following syntax: 101 | // 102 | // fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) 103 | func Println(a ...interface{}) (n int, err error) { 104 | return fmt.Println(convertArgs(a)...) 105 | } 106 | 107 | // Sprint is a wrapper for fmt.Sprint that treats each argument as if it were 108 | // passed with a default Formatter interface returned by NewFormatter. It 109 | // returns the resulting string. See NewFormatter for formatting details. 110 | // 111 | // This function is shorthand for the following syntax: 112 | // 113 | // fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) 114 | func Sprint(a ...interface{}) string { 115 | return fmt.Sprint(convertArgs(a)...) 116 | } 117 | 118 | // Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were 119 | // passed with a default Formatter interface returned by NewFormatter. It 120 | // returns the resulting string. See NewFormatter for formatting details. 121 | // 122 | // This function is shorthand for the following syntax: 123 | // 124 | // fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 125 | func Sprintf(format string, a ...interface{}) string { 126 | return fmt.Sprintf(format, convertArgs(a)...) 127 | } 128 | 129 | // Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it 130 | // were passed with a default Formatter interface returned by NewFormatter. It 131 | // returns the resulting string. See NewFormatter for formatting details. 132 | // 133 | // This function is shorthand for the following syntax: 134 | // 135 | // fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) 136 | func Sprintln(a ...interface{}) string { 137 | return fmt.Sprintln(convertArgs(a)...) 138 | } 139 | 140 | // convertArgs accepts a slice of arguments and returns a slice of the same 141 | // length with each argument converted to a default spew Formatter interface. 142 | func convertArgs(args []interface{}) (formatters []interface{}) { 143 | formatters = make([]interface{}, len(args)) 144 | for index, arg := range args { 145 | formatters[index] = NewFormatter(arg) 146 | } 147 | return formatters 148 | } 149 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/accessors.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // arrayAccesRegexString is the regex used to extract the array number 11 | // from the access path 12 | const arrayAccesRegexString = `^(.+)\[([0-9]+)\]$` 13 | 14 | // arrayAccesRegex is the compiled arrayAccesRegexString 15 | var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString) 16 | 17 | // Get gets the value using the specified selector and 18 | // returns it inside a new Obj object. 19 | // 20 | // If it cannot find the value, Get will return a nil 21 | // value inside an instance of Obj. 22 | // 23 | // Get can only operate directly on map[string]interface{} and []interface. 24 | // 25 | // Example 26 | // 27 | // To access the title of the third chapter of the second book, do: 28 | // 29 | // o.Get("books[1].chapters[2].title") 30 | func (m Map) Get(selector string) *Value { 31 | rawObj := access(m, selector, nil, false, false) 32 | return &Value{data: rawObj} 33 | } 34 | 35 | // Set sets the value using the specified selector and 36 | // returns the object on which Set was called. 37 | // 38 | // Set can only operate directly on map[string]interface{} and []interface 39 | // 40 | // Example 41 | // 42 | // To set the title of the third chapter of the second book, do: 43 | // 44 | // o.Set("books[1].chapters[2].title","Time to Go") 45 | func (m Map) Set(selector string, value interface{}) Map { 46 | access(m, selector, value, true, false) 47 | return m 48 | } 49 | 50 | // access accesses the object using the selector and performs the 51 | // appropriate action. 52 | func access(current, selector, value interface{}, isSet, panics bool) interface{} { 53 | 54 | switch selector.(type) { 55 | case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 56 | 57 | if array, ok := current.([]interface{}); ok { 58 | index := intFromInterface(selector) 59 | 60 | if index >= len(array) { 61 | if panics { 62 | panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array))) 63 | } 64 | return nil 65 | } 66 | 67 | return array[index] 68 | } 69 | 70 | return nil 71 | 72 | case string: 73 | 74 | selStr := selector.(string) 75 | selSegs := strings.SplitN(selStr, PathSeparator, 2) 76 | thisSel := selSegs[0] 77 | index := -1 78 | var err error 79 | 80 | // https://github.com/stretchr/objx/issues/12 81 | if strings.Contains(thisSel, "[") { 82 | 83 | arrayMatches := arrayAccesRegex.FindStringSubmatch(thisSel) 84 | 85 | if len(arrayMatches) > 0 { 86 | 87 | // Get the key into the map 88 | thisSel = arrayMatches[1] 89 | 90 | // Get the index into the array at the key 91 | index, err = strconv.Atoi(arrayMatches[2]) 92 | 93 | if err != nil { 94 | // This should never happen. If it does, something has gone 95 | // seriously wrong. Panic. 96 | panic("objx: Array index is not an integer. Must use array[int].") 97 | } 98 | 99 | } 100 | } 101 | 102 | if curMap, ok := current.(Map); ok { 103 | current = map[string]interface{}(curMap) 104 | } 105 | 106 | // get the object in question 107 | switch current.(type) { 108 | case map[string]interface{}: 109 | curMSI := current.(map[string]interface{}) 110 | if len(selSegs) <= 1 && isSet { 111 | curMSI[thisSel] = value 112 | return nil 113 | } else { 114 | current = curMSI[thisSel] 115 | } 116 | default: 117 | current = nil 118 | } 119 | 120 | if current == nil && panics { 121 | panic(fmt.Sprintf("objx: '%v' invalid on object.", selector)) 122 | } 123 | 124 | // do we need to access the item of an array? 125 | if index > -1 { 126 | if array, ok := current.([]interface{}); ok { 127 | if index < len(array) { 128 | current = array[index] 129 | } else { 130 | if panics { 131 | panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array))) 132 | } 133 | current = nil 134 | } 135 | } 136 | } 137 | 138 | if len(selSegs) > 1 { 139 | current = access(current, selSegs[1], value, isSet, panics) 140 | } 141 | 142 | } 143 | 144 | return current 145 | 146 | } 147 | 148 | // intFromInterface converts an interface object to the largest 149 | // representation of an unsigned integer using a type switch and 150 | // assertions 151 | func intFromInterface(selector interface{}) int { 152 | var value int 153 | switch selector.(type) { 154 | case int: 155 | value = selector.(int) 156 | case int8: 157 | value = int(selector.(int8)) 158 | case int16: 159 | value = int(selector.(int16)) 160 | case int32: 161 | value = int(selector.(int32)) 162 | case int64: 163 | value = int(selector.(int64)) 164 | case uint: 165 | value = int(selector.(uint)) 166 | case uint8: 167 | value = int(selector.(uint8)) 168 | case uint16: 169 | value = int(selector.(uint16)) 170 | case uint32: 171 | value = int(selector.(uint32)) 172 | case uint64: 173 | value = int(selector.(uint64)) 174 | default: 175 | panic("objx: array access argument is not an integer type (this should never happen)") 176 | } 177 | 178 | return value 179 | } 180 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/constants.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | const ( 4 | // PathSeparator is the character used to separate the elements 5 | // of the keypath. 6 | // 7 | // For example, `location.address.city` 8 | PathSeparator string = "." 9 | 10 | // SignatureSeparator is the character that is used to 11 | // separate the Base64 string from the security signature. 12 | SignatureSeparator = "_" 13 | ) 14 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/conversions.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "bytes" 5 | "encoding/base64" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "net/url" 10 | ) 11 | 12 | // JSON converts the contained object to a JSON string 13 | // representation 14 | func (m Map) JSON() (string, error) { 15 | 16 | result, err := json.Marshal(m) 17 | 18 | if err != nil { 19 | err = errors.New("objx: JSON encode failed with: " + err.Error()) 20 | } 21 | 22 | return string(result), err 23 | 24 | } 25 | 26 | // MustJSON converts the contained object to a JSON string 27 | // representation and panics if there is an error 28 | func (m Map) MustJSON() string { 29 | result, err := m.JSON() 30 | if err != nil { 31 | panic(err.Error()) 32 | } 33 | return result 34 | } 35 | 36 | // Base64 converts the contained object to a Base64 string 37 | // representation of the JSON string representation 38 | func (m Map) Base64() (string, error) { 39 | 40 | var buf bytes.Buffer 41 | 42 | jsonData, err := m.JSON() 43 | if err != nil { 44 | return "", err 45 | } 46 | 47 | encoder := base64.NewEncoder(base64.StdEncoding, &buf) 48 | encoder.Write([]byte(jsonData)) 49 | encoder.Close() 50 | 51 | return buf.String(), nil 52 | 53 | } 54 | 55 | // MustBase64 converts the contained object to a Base64 string 56 | // representation of the JSON string representation and panics 57 | // if there is an error 58 | func (m Map) MustBase64() string { 59 | result, err := m.Base64() 60 | if err != nil { 61 | panic(err.Error()) 62 | } 63 | return result 64 | } 65 | 66 | // SignedBase64 converts the contained object to a Base64 string 67 | // representation of the JSON string representation and signs it 68 | // using the provided key. 69 | func (m Map) SignedBase64(key string) (string, error) { 70 | 71 | base64, err := m.Base64() 72 | if err != nil { 73 | return "", err 74 | } 75 | 76 | sig := HashWithKey(base64, key) 77 | 78 | return base64 + SignatureSeparator + sig, nil 79 | 80 | } 81 | 82 | // MustSignedBase64 converts the contained object to a Base64 string 83 | // representation of the JSON string representation and signs it 84 | // using the provided key and panics if there is an error 85 | func (m Map) MustSignedBase64(key string) string { 86 | result, err := m.SignedBase64(key) 87 | if err != nil { 88 | panic(err.Error()) 89 | } 90 | return result 91 | } 92 | 93 | /* 94 | URL Query 95 | ------------------------------------------------ 96 | */ 97 | 98 | // URLValues creates a url.Values object from an Obj. This 99 | // function requires that the wrapped object be a map[string]interface{} 100 | func (m Map) URLValues() url.Values { 101 | 102 | vals := make(url.Values) 103 | 104 | for k, v := range m { 105 | //TODO: can this be done without sprintf? 106 | vals.Set(k, fmt.Sprintf("%v", v)) 107 | } 108 | 109 | return vals 110 | } 111 | 112 | // URLQuery gets an encoded URL query representing the given 113 | // Obj. This function requires that the wrapped object be a 114 | // map[string]interface{} 115 | func (m Map) URLQuery() (string, error) { 116 | return m.URLValues().Encode(), nil 117 | } 118 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/doc.go: -------------------------------------------------------------------------------- 1 | // objx - Go package for dealing with maps, slices, JSON and other data. 2 | // 3 | // Overview 4 | // 5 | // Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes 6 | // a powerful `Get` method (among others) that allows you to easily and quickly get 7 | // access to data within the map, without having to worry too much about type assertions, 8 | // missing data, default values etc. 9 | // 10 | // Pattern 11 | // 12 | // Objx uses a preditable pattern to make access data from within `map[string]interface{}'s 13 | // easy. 14 | // 15 | // Call one of the `objx.` functions to create your `objx.Map` to get going: 16 | // 17 | // m, err := objx.FromJSON(json) 18 | // 19 | // NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, 20 | // the rest will be optimistic and try to figure things out without panicking. 21 | // 22 | // Use `Get` to access the value you're interested in. You can use dot and array 23 | // notation too: 24 | // 25 | // m.Get("places[0].latlng") 26 | // 27 | // Once you have saught the `Value` you're interested in, you can use the `Is*` methods 28 | // to determine its type. 29 | // 30 | // if m.Get("code").IsStr() { /* ... */ } 31 | // 32 | // Or you can just assume the type, and use one of the strong type methods to 33 | // extract the real value: 34 | // 35 | // m.Get("code").Int() 36 | // 37 | // If there's no value there (or if it's the wrong type) then a default value 38 | // will be returned, or you can be explicit about the default value. 39 | // 40 | // Get("code").Int(-1) 41 | // 42 | // If you're dealing with a slice of data as a value, Objx provides many useful 43 | // methods for iterating, manipulating and selecting that data. You can find out more 44 | // by exploring the index below. 45 | // 46 | // Reading data 47 | // 48 | // A simple example of how to use Objx: 49 | // 50 | // // use MustFromJSON to make an objx.Map from some JSON 51 | // m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) 52 | // 53 | // // get the details 54 | // name := m.Get("name").Str() 55 | // age := m.Get("age").Int() 56 | // 57 | // // get their nickname (or use their name if they 58 | // // don't have one) 59 | // nickname := m.Get("nickname").Str(name) 60 | // 61 | // Ranging 62 | // 63 | // Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For 64 | // example, to `range` the data, do what you would expect: 65 | // 66 | // m := objx.MustFromJSON(json) 67 | // for key, value := range m { 68 | // 69 | // /* ... do your magic ... */ 70 | // 71 | // } 72 | package objx 73 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/map.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/json" 6 | "errors" 7 | "io/ioutil" 8 | "net/url" 9 | "strings" 10 | ) 11 | 12 | // MSIConvertable is an interface that defines methods for converting your 13 | // custom types to a map[string]interface{} representation. 14 | type MSIConvertable interface { 15 | // MSI gets a map[string]interface{} (msi) representing the 16 | // object. 17 | MSI() map[string]interface{} 18 | } 19 | 20 | // Map provides extended functionality for working with 21 | // untyped data, in particular map[string]interface (msi). 22 | type Map map[string]interface{} 23 | 24 | // Value returns the internal value instance 25 | func (m Map) Value() *Value { 26 | return &Value{data: m} 27 | } 28 | 29 | // Nil represents a nil Map. 30 | var Nil Map = New(nil) 31 | 32 | // New creates a new Map containing the map[string]interface{} in the data argument. 33 | // If the data argument is not a map[string]interface, New attempts to call the 34 | // MSI() method on the MSIConvertable interface to create one. 35 | func New(data interface{}) Map { 36 | if _, ok := data.(map[string]interface{}); !ok { 37 | if converter, ok := data.(MSIConvertable); ok { 38 | data = converter.MSI() 39 | } else { 40 | return nil 41 | } 42 | } 43 | return Map(data.(map[string]interface{})) 44 | } 45 | 46 | // MSI creates a map[string]interface{} and puts it inside a new Map. 47 | // 48 | // The arguments follow a key, value pattern. 49 | // 50 | // Panics 51 | // 52 | // Panics if any key arugment is non-string or if there are an odd number of arguments. 53 | // 54 | // Example 55 | // 56 | // To easily create Maps: 57 | // 58 | // m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true)) 59 | // 60 | // // creates an Map equivalent to 61 | // m := objx.New(map[string]interface{}{"name": "Mat", "age": 29, "subobj": map[string]interface{}{"active": true}}) 62 | func MSI(keyAndValuePairs ...interface{}) Map { 63 | 64 | newMap := make(map[string]interface{}) 65 | keyAndValuePairsLen := len(keyAndValuePairs) 66 | 67 | if keyAndValuePairsLen%2 != 0 { 68 | panic("objx: MSI must have an even number of arguments following the 'key, value' pattern.") 69 | } 70 | 71 | for i := 0; i < keyAndValuePairsLen; i = i + 2 { 72 | 73 | key := keyAndValuePairs[i] 74 | value := keyAndValuePairs[i+1] 75 | 76 | // make sure the key is a string 77 | keyString, keyStringOK := key.(string) 78 | if !keyStringOK { 79 | panic("objx: MSI must follow 'string, interface{}' pattern. " + keyString + " is not a valid key.") 80 | } 81 | 82 | newMap[keyString] = value 83 | 84 | } 85 | 86 | return New(newMap) 87 | } 88 | 89 | // ****** Conversion Constructors 90 | 91 | // MustFromJSON creates a new Map containing the data specified in the 92 | // jsonString. 93 | // 94 | // Panics if the JSON is invalid. 95 | func MustFromJSON(jsonString string) Map { 96 | o, err := FromJSON(jsonString) 97 | 98 | if err != nil { 99 | panic("objx: MustFromJSON failed with error: " + err.Error()) 100 | } 101 | 102 | return o 103 | } 104 | 105 | // FromJSON creates a new Map containing the data specified in the 106 | // jsonString. 107 | // 108 | // Returns an error if the JSON is invalid. 109 | func FromJSON(jsonString string) (Map, error) { 110 | 111 | var data interface{} 112 | err := json.Unmarshal([]byte(jsonString), &data) 113 | 114 | if err != nil { 115 | return Nil, err 116 | } 117 | 118 | return New(data), nil 119 | 120 | } 121 | 122 | // FromBase64 creates a new Obj containing the data specified 123 | // in the Base64 string. 124 | // 125 | // The string is an encoded JSON string returned by Base64 126 | func FromBase64(base64String string) (Map, error) { 127 | 128 | decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String)) 129 | 130 | decoded, err := ioutil.ReadAll(decoder) 131 | if err != nil { 132 | return nil, err 133 | } 134 | 135 | return FromJSON(string(decoded)) 136 | } 137 | 138 | // MustFromBase64 creates a new Obj containing the data specified 139 | // in the Base64 string and panics if there is an error. 140 | // 141 | // The string is an encoded JSON string returned by Base64 142 | func MustFromBase64(base64String string) Map { 143 | 144 | result, err := FromBase64(base64String) 145 | 146 | if err != nil { 147 | panic("objx: MustFromBase64 failed with error: " + err.Error()) 148 | } 149 | 150 | return result 151 | } 152 | 153 | // FromSignedBase64 creates a new Obj containing the data specified 154 | // in the Base64 string. 155 | // 156 | // The string is an encoded JSON string returned by SignedBase64 157 | func FromSignedBase64(base64String, key string) (Map, error) { 158 | parts := strings.Split(base64String, SignatureSeparator) 159 | if len(parts) != 2 { 160 | return nil, errors.New("objx: Signed base64 string is malformed.") 161 | } 162 | 163 | sig := HashWithKey(parts[0], key) 164 | if parts[1] != sig { 165 | return nil, errors.New("objx: Signature for base64 data does not match.") 166 | } 167 | 168 | return FromBase64(parts[0]) 169 | } 170 | 171 | // MustFromSignedBase64 creates a new Obj containing the data specified 172 | // in the Base64 string and panics if there is an error. 173 | // 174 | // The string is an encoded JSON string returned by Base64 175 | func MustFromSignedBase64(base64String, key string) Map { 176 | 177 | result, err := FromSignedBase64(base64String, key) 178 | 179 | if err != nil { 180 | panic("objx: MustFromSignedBase64 failed with error: " + err.Error()) 181 | } 182 | 183 | return result 184 | } 185 | 186 | // FromURLQuery generates a new Obj by parsing the specified 187 | // query. 188 | // 189 | // For queries with multiple values, the first value is selected. 190 | func FromURLQuery(query string) (Map, error) { 191 | 192 | vals, err := url.ParseQuery(query) 193 | 194 | if err != nil { 195 | return nil, err 196 | } 197 | 198 | m := make(map[string]interface{}) 199 | for k, vals := range vals { 200 | m[k] = vals[0] 201 | } 202 | 203 | return New(m), nil 204 | } 205 | 206 | // MustFromURLQuery generates a new Obj by parsing the specified 207 | // query. 208 | // 209 | // For queries with multiple values, the first value is selected. 210 | // 211 | // Panics if it encounters an error 212 | func MustFromURLQuery(query string) Map { 213 | 214 | o, err := FromURLQuery(query) 215 | 216 | if err != nil { 217 | panic("objx: MustFromURLQuery failed with error: " + err.Error()) 218 | } 219 | 220 | return o 221 | 222 | } 223 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/mutations.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | // Exclude returns a new Map with the keys in the specified []string 4 | // excluded. 5 | func (d Map) Exclude(exclude []string) Map { 6 | 7 | excluded := make(Map) 8 | for k, v := range d { 9 | var shouldInclude bool = true 10 | for _, toExclude := range exclude { 11 | if k == toExclude { 12 | shouldInclude = false 13 | break 14 | } 15 | } 16 | if shouldInclude { 17 | excluded[k] = v 18 | } 19 | } 20 | 21 | return excluded 22 | } 23 | 24 | // Copy creates a shallow copy of the Obj. 25 | func (m Map) Copy() Map { 26 | copied := make(map[string]interface{}) 27 | for k, v := range m { 28 | copied[k] = v 29 | } 30 | return New(copied) 31 | } 32 | 33 | // Merge blends the specified map with a copy of this map and returns the result. 34 | // 35 | // Keys that appear in both will be selected from the specified map. 36 | // This method requires that the wrapped object be a map[string]interface{} 37 | func (m Map) Merge(merge Map) Map { 38 | return m.Copy().MergeHere(merge) 39 | } 40 | 41 | // Merge blends the specified map with this map and returns the current map. 42 | // 43 | // Keys that appear in both will be selected from the specified map. The original map 44 | // will be modified. This method requires that 45 | // the wrapped object be a map[string]interface{} 46 | func (m Map) MergeHere(merge Map) Map { 47 | 48 | for k, v := range merge { 49 | m[k] = v 50 | } 51 | 52 | return m 53 | 54 | } 55 | 56 | // Transform builds a new Obj giving the transformer a chance 57 | // to change the keys and values as it goes. This method requires that 58 | // the wrapped object be a map[string]interface{} 59 | func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map { 60 | newMap := make(map[string]interface{}) 61 | for k, v := range m { 62 | modifiedKey, modifiedVal := transformer(k, v) 63 | newMap[modifiedKey] = modifiedVal 64 | } 65 | return New(newMap) 66 | } 67 | 68 | // TransformKeys builds a new map using the specified key mapping. 69 | // 70 | // Unspecified keys will be unaltered. 71 | // This method requires that the wrapped object be a map[string]interface{} 72 | func (m Map) TransformKeys(mapping map[string]string) Map { 73 | return m.Transform(func(key string, value interface{}) (string, interface{}) { 74 | 75 | if newKey, ok := mapping[key]; ok { 76 | return newKey, value 77 | } 78 | 79 | return key, value 80 | }) 81 | } 82 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/security.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "crypto/sha1" 5 | "encoding/hex" 6 | ) 7 | 8 | // HashWithKey hashes the specified string using the security 9 | // key. 10 | func HashWithKey(data, key string) string { 11 | hash := sha1.New() 12 | hash.Write([]byte(data + ":" + key)) 13 | return hex.EncodeToString(hash.Sum(nil)) 14 | } 15 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/tests.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | // Has gets whether there is something at the specified selector 4 | // or not. 5 | // 6 | // If m is nil, Has will always return false. 7 | func (m Map) Has(selector string) bool { 8 | if m == nil { 9 | return false 10 | } 11 | return !m.Get(selector).IsNil() 12 | } 13 | 14 | // IsNil gets whether the data is nil or not. 15 | func (v *Value) IsNil() bool { 16 | return v == nil || v.data == nil 17 | } 18 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/vendor/github.com/stretchr/objx/value.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | // Value provides methods for extracting interface{} data in various 4 | // types. 5 | type Value struct { 6 | // data contains the raw data being managed by this Value 7 | data interface{} 8 | } 9 | 10 | // Data returns the raw data contained by this Value 11 | func (v *Value) Data() interface{} { 12 | return v.data 13 | } 14 | -------------------------------------------------------------------------------- /vendor/manifest: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "dependencies": [ 4 | { 5 | "importpath": "github.com/stretchr/testify", 6 | "repository": "https://github.com/stretchr/testify", 7 | "vcs": "git", 8 | "revision": "2402e8e7a02fc811447d11f881aa9746cdc57983", 9 | "branch": "master", 10 | "notests": true 11 | } 12 | ] 13 | } --------------------------------------------------------------------------------