Repository: census-ecosystem/opencensus-go-exporter-stackdriver
Pull request #200 (2019-09-05 14:37:00)

Enable test coverage.

bogdandrutu: @songy23 don't know exactly how. I need to read more details.
bogdandrutu: @songy23 for the moment I enabled coverage without the script to check that we don't have empty packages (no tests). I will do that later, but this PR got bigger and a lot of improvements will be blocked.
codecov-io: # [Codecov](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200?src=pr&el=h1) Report
> :exclamation: No coverage uploaded for pull request base (`master@dd3a274`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference#section-missing-base-commit).
> The diff coverage is `50%`.

[![Impacted file tree graph](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200/graphs/tree.svg?width=650&token=os6w8WyKNy&height=150&src=pr)](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200?src=pr&el=tree)

```diff
@@           Coverage Diff            @@
##             master    #200   +/-   ##
========================================
  Coverage          ?   71.3%           
========================================
  Files             ?      15           
  Lines             ?    1582           
  Branches          ?       0           
========================================
  Hits              ?    1128           
  Misses            ?     374           
  Partials          ?      80
```


| [Impacted Files](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [monitoredresource/gcp\_metadata\_config.go](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200/diff?src=pr&el=tree#diff-bW9uaXRvcmVkcmVzb3VyY2UvZ2NwX21ldGFkYXRhX2NvbmZpZy5nbw==) | `0% <ø> (ø)` | |
| [resource.go](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200/diff?src=pr&el=tree#diff-cmVzb3VyY2UuZ28=) | `100% <ø> (ø)` | |
| [trace.go](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200/diff?src=pr&el=tree#diff-dHJhY2UuZ28=) | `59.09% <0%> (ø)` | |
| [monitoredresource/aws\_identity\_doc\_utils.go](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200/diff?src=pr&el=tree#diff-bW9uaXRvcmVkcmVzb3VyY2UvYXdzX2lkZW50aXR5X2RvY191dGlscy5nbw==) | `0% <0%> (ø)` | |
| [stackdriver.go](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200/diff?src=pr&el=tree#diff-c3RhY2tkcml2ZXIuZ28=) | `30.92% <100%> (ø)` | |
| [stats.go](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200/diff?src=pr&el=tree#diff-c3RhdHMuZ28=) | `77.35% <100%> (ø)` | |
| [metrics\_proto.go](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200/diff?src=pr&el=tree#diff-bWV0cmljc19wcm90by5nbw==) | `84.08% <50%> (ø)` | |
| [metrics\_batcher.go](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200/diff?src=pr&el=tree#diff-bWV0cmljc19iYXRjaGVyLmdv) | `51.42% <66.66%> (ø)` | |
| [metrics.go](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200/diff?src=pr&el=tree#diff-bWV0cmljcy5nbw==) | `80.37% <75%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200?src=pr&el=footer). Last update [dd3a274...858c25d](https://codecov.io/gh/census-ecosystem/opencensus-go-exporter-stackdriver/pull/200?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.gitignore b/.gitignore
index c0ee15b..c5a5414 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,10 @@
-/vendor/
+# GoLand IDEA
 /.idea/
 *.iml
 
+# VS Code
+.vscode
 
+# Coverage
+coverage.txt
+coverage.html
diff --git a/.travis.yml b/.travis.yml
index babe4ee..7da9441 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
 language: go
 
 go:
-  - 1.11.x
+  - 1.12.x
 
 go_import_path: contrib.go.opencensus.io/exporter/stackdriver
 
@@ -9,16 +9,12 @@ env:
   global:
     GO111MODULE=on
 
-before_script:
-  - GO_FILES=$(find . -iname '*.go' | grep -v /vendor/)  # All the .go files, excluding vendor/ if any
-  - PKGS=$(go list ./... | grep -v /vendor/)             # All the import paths, excluding vendor/ if any
+install:
+  - go mod download
+  - make install-tools
 
 script:
-  - go build ./...                    # Ensure dependency updates don't break build
-  - if [ -n "$(gofmt -s -l $GO_FILES)" ]; then echo "gofmt the following files:"; gofmt -s -l $GO_FILES; exit 1; fi
-  - go vet ./...
-  - go test -v -race $PKGS            # Run all the tests with the race detector enabled
-  - GO111MODULE=off go get -t ./...
-  - GO111MODULE=off go build ./...
-  - GO111MODULE=off go test -v -race $PKGS            # Make sure tests still pass when not using Go modules.
-  - 'if [[ $TRAVIS_GO_VERSION = 1.8* ]]; then ! golint ./... | grep -vE "(_mock|_string|\.pb)\.go:"; fi'
+  - make travis-ci
+
+after_success:
+  - bash <(curl -s https://codecov.io/bash)
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 2e11d22..cc08185 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 # TODO: Fix this on windows.
 ALL_SRC := $(shell find . -name '*.go' \
-								-not -path './vendor/*' \
-								-not -path '*/gen-go/*' \
+								-not -path '*/internal/testpb/*' \
+								-not -name 'tools.go' \
 								-type f | sort)
 ALL_PKGS := $(shell go list $(sort $(dir $(ALL_SRC))))
 
@@ -10,20 +10,21 @@ GOTEST_OPT_WITH_COVERAGE = $(GOTEST_OPT) -coverprofile=coverage.txt -covermode=a
 GOTEST=go test
 GOFMT=gofmt
 GOLINT=golint
+GOIMPORTS=goimports
 GOVET=go vet
 EMBEDMD=embedmd
+STATICCHECK=staticcheck
 # TODO decide if we need to change these names.
 README_FILES := $(shell find . -name '*README.md' | sort | tr '\n' ' ')
 
+.DEFAULT_GOAL := defaul-goal
 
-.DEFAULT_GOAL := fmt-lint-vet-embedmd-test
+.PHONY: defaul-goal
+defaul-goal: fmt lint vet embedmd goimports staticcheck test
 
-.PHONY: fmt-lint-vet-embedmd-test
-fmt-lint-vet-embedmd-test: fmt lint vet embedmd test
-
-# TODO enable test-with-coverage in tavis
+# TODO: enable test-with-cover when find out why "scripts/check-test-files.sh: 4: set: Illegal option -o pipefail"
 .PHONY: travis-ci
-travis-ci: fmt lint vet embedmd test test-386
+travis-ci: fmt lint vet embedmd goimports staticcheck test test-386 test-with-coverage
 
 all-pkgs:
 	@echo $(ALL_PKGS) | tr ' ' '\n' | sort
@@ -41,7 +42,19 @@ test-386:
 
 .PHONY: test-with-coverage
 test-with-coverage:
+	@echo pre-compiling tests
+	@time go test -i $(ALL_PKGS)
+	$(GOTEST) $(GOTEST_OPT_WITH_COVERAGE) $(ALL_PKGS)
+	go tool cover -html=coverage.txt -o coverage.html
+
+.PHONY: test-with-cover
+test-with-cover:
+	@echo Verifying that all packages have test files to count in coverage
+	@scripts/check-test-files.sh $(subst contrib.go.opencensus.io/exporter/stackdriver,./,$(ALL_PKGS))
+	@echo pre-compiling tests
+	@time go test -i $(ALL_PKGS)
 	$(GOTEST) $(GOTEST_OPT_WITH_COVERAGE) $(ALL_PKGS)
+	go tool cover -html=coverage.txt -o coverage.html
 
 .PHONY: fmt
 fmt:
@@ -88,8 +101,25 @@ embedmd:
 	    echo "Embedmd finished successfully"; \
 	fi
 
+.PHONY: goimports
+goimports:
+	@IMPORTSOUT=`$(GOIMPORTS) -d . 2>&1`; \
+	if [ "$$IMPORTSOUT" ]; then \
+		echo "$(GOIMPORTS) FAILED => fix the following goimports errors:\n"; \
+		echo "$$IMPORTSOUT\n"; \
+		exit 1; \
+	else \
+	    echo "Goimports finished successfully"; \
+	fi
+
+.PHONY: staticcheck
+staticcheck:
+	$(STATICCHECK) ./...
+
 .PHONY: install-tools
 install-tools:
-	go get -u golang.org/x/tools/cmd/cover
-	go get -u golang.org/x/lint/golint
-	go get -u github.com/rakyll/embedmd
+	GO111MODULE=on go install \
+		golang.org/x/lint/golint \
+		golang.org/x/tools/cmd/goimports \
+		github.com/rakyll/embedmd \
+		honnef.co/go/tools/cmd/staticcheck
diff --git a/go.mod b/go.mod
index df0956d..0d05695 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
 	github.com/census-instrumentation/opencensus-proto v0.2.1
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/golang/protobuf v1.3.2
+	github.com/google/addlicense v0.0.0-20190510175307-22550fa7c1b0 // indirect
 	github.com/google/go-cmp v0.3.1
 	github.com/hashicorp/golang-lru v0.5.3 // indirect
 	github.com/stretchr/testify v1.3.0 // indirect
diff --git a/go.sum b/go.sum
index d2cc0c7..d08b19a 100644
--- a/go.sum
+++ b/go.sum
@@ -5,12 +5,14 @@ cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
 cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 cloud.google.com/go v0.43.0 h1:banaiRPAM8kUVYneOSkhgcDsLzEvL25FinuiSZaH/2w=
 cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/aws/aws-sdk-go v1.22.1 h1://WJvJi9iq/i5TWHuK3hIC23xCZYH7Qv7SIN2vZVqxY=
 github.com/aws/aws-sdk-go v1.22.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -27,6 +29,8 @@ github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/addlicense v0.0.0-20190510175307-22550fa7c1b0 h1:ydbHzabf84uucKri5fcfiqYxGg+rYgP/zQfLLN8lyP0=
+github.com/google/addlicense v0.0.0-20190510175307-22550fa7c1b0/go.mod h1:QtPG26W17m+OIQgE6gQ24gC1M6pUaMBAbFrTIDtwG/E=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
@@ -50,9 +54,12 @@ github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8
 github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rakyll/embedmd v0.0.0-20171029212350-c8060a0752a2 h1:1jfy6i1g66ijpffgfaF/7pIFYZnSZzvo9P9DFkFmRIM=
+github.com/rakyll/embedmd v0.0.0-20171029212350-c8060a0752a2/go.mod h1:7jOTMgqac46PZcF54q6l2hkLEG8op93fZu61KmxWDV4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -68,6 +75,7 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI=
 golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -127,6 +135,7 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn
 golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0 h1:Dh6fw+p6FyRl5x/FvNswO1ji0lIGzm3KP8Y9VkS9PTE=
 golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
@@ -159,5 +168,6 @@ google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
diff --git a/internal/tools.go b/internal/tools.go
new file mode 100644
index 0000000..ac2d973
--- /dev/null
+++ b/internal/tools.go
@@ -0,0 +1,30 @@
+// Copyright 2019, OpenCensus Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// +build tools
+
+package internal
+
+// This file follows the recommendation at
+// https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module
+// on how to pin tooling dependencies to a go.mod file.
+// This ensures that all systems use the same version of tools in addition to regular dependencies.
+
+import (
+	_ "github.com/jstemmer/go-junit-report"
+	_ "golang.org/x/lint/golint"
+	_ "golang.org/x/tools/cmd/goimports"
+	_ "honnef.co/go/tools/cmd/staticcheck"
+)
diff --git a/metrics.go b/metrics.go
index 3129ce4..d9cddc7 100644
--- a/metrics.go
+++ b/metrics.go
@@ -21,7 +21,6 @@ directly to Stackdriver Metrics.
 
 import (
 	"context"
-	"errors"
 	"fmt"
 
 	"github.com/golang/protobuf/proto"
@@ -39,11 +38,6 @@ import (
 	"go.opencensus.io/resource"
 )
 
-var (
-	errLableExtraction       = errors.New("error extracting labels")
-	errUnspecifiedMetricKind = errors.New("metric kind is unpsecified")
-)
-
 const (
 	exemplarAttachmentTypeString  = "type.googleapis.com/google.protobuf.StringValue"
 	exemplarAttachmentTypeSpanCtx = "type.googleapis.com/google.monitoring.v3.SpanContext"
@@ -74,7 +68,7 @@ func (se *statsExporter) handleMetricsUpload(metrics []*metricdata.Metric) {
 }
 
 func (se *statsExporter) uploadMetrics(metrics []*metricdata.Metric) error {
-	ctx, cancel := se.o.newContextWithTimeout()
+	ctx, cancel := newContextWithTimeout(se.o.Context, se.o.Timeout)
 	defer cancel()
 
 	ctx, span := trace.StartSpan(
@@ -182,7 +176,7 @@ func metricLabelsToTsLabels(defaults map[string]labelValue, labelKeys []metricda
 
 	// Perform this sanity check now.
 	if len(labelKeys) != len(labelValues) {
-		return labels, fmt.Errorf("Length mismatch: len(labelKeys)=%d len(labelValues)=%d", len(labelKeys), len(labelValues))
+		return labels, fmt.Errorf("length mismatch: len(labelKeys)=%d len(labelValues)=%d", len(labelKeys), len(labelValues))
 	}
 
 	for i, labelKey := range labelKeys {
@@ -461,11 +455,9 @@ func metricExemplarToPbExemplar(exemplar *metricdata.Exemplar, projectID string)
 func attachmentsToPbAttachments(attachments metricdata.Attachments, projectID string) []*any.Any {
 	var pbAttachments []*any.Any
 	for _, v := range attachments {
-		switch v.(type) {
-		case trace.SpanContext:
-			spanCtx, _ := v.(trace.SpanContext)
+		if spanCtx, succ := v.(trace.SpanContext); succ {
 			pbAttachments = append(pbAttachments, toPbSpanCtxAttachment(spanCtx, projectID))
-		default:
+		} else {
 			// Treat everything else as plain string for now.
 			// TODO(songy23): add support for dropped label attachments.
 			pbAttachments = append(pbAttachments, toPbStringAttachment(v))
diff --git a/metrics_batcher.go b/metrics_batcher.go
index 3b8bb3c..921d967 100644
--- a/metrics_batcher.go
+++ b/metrics_batcher.go
@@ -24,17 +24,17 @@ import (
 )
 
 type metricsBatcher struct {
-	projectID string
-	allReqs   []*monitoringpb.CreateTimeSeriesRequest
-	allTss    []*monitoringpb.TimeSeries
-	allErrs   []error
+	projectName string
+	allReqs     []*monitoringpb.CreateTimeSeriesRequest
+	allTss      []*monitoringpb.TimeSeries
+	allErrs     []error
 	// Counts all dropped TimeSeries by this exporter.
 	droppedTimeSeries int
 }
 
 func newMetricsBatcher(projectID string) *metricsBatcher {
 	return &metricsBatcher{
-		projectID:         projectID,
+		projectName:       fmt.Sprintf("projects/%s", projectID),
 		allTss:            make([]*monitoringpb.TimeSeries, 0, maxTimeSeriesPerUpload),
 		droppedTimeSeries: 0,
 	}
@@ -49,7 +49,7 @@ func (mb *metricsBatcher) addTimeSeries(ts *monitoringpb.TimeSeries) {
 	mb.allTss = append(mb.allTss, ts)
 	if len(mb.allTss) == maxTimeSeriesPerUpload {
 		mb.allReqs = append(mb.allReqs, &monitoringpb.CreateTimeSeriesRequest{
-			Name:       monitoring.MetricProjectPath(mb.projectID),
+			Name:       mb.projectName,
 			TimeSeries: mb.allTss,
 		})
 		mb.allTss = make([]*monitoringpb.TimeSeries, 0, maxTimeSeriesPerUpload)
@@ -60,7 +60,7 @@ func (mb *metricsBatcher) export(ctx context.Context, mc *monitoring.MetricClien
 	// Last batch, if any.
 	if len(mb.allTss) > 0 {
 		mb.allReqs = append(mb.allReqs, &monitoringpb.CreateTimeSeriesRequest{
-			Name:       monitoring.MetricProjectPath(mb.projectID),
+			Name:       mb.projectName,
 			TimeSeries: mb.allTss,
 		})
 	}
diff --git a/metrics_proto.go b/metrics_proto.go
index cf80548..10da94c 100644
--- a/metrics_proto.go
+++ b/metrics_proto.go
@@ -54,7 +54,7 @@ func (se *statsExporter) PushMetricsProto(ctx context.Context, node *commonpb.No
 		return 0, errNilMetricOrMetricDescriptor
 	}
 
-	ctx, cancel := se.o.newContextWithTimeout()
+	ctx, cancel := newContextWithTimeout(ctx, se.o.Timeout)
 	defer cancel()
 
 	// Caches the resources seen so far
@@ -284,7 +284,7 @@ func labelsPerTimeSeries(defaults map[string]labelValue, labelKeys []string, lab
 
 	// Perform this sanity check now.
 	if len(labelKeys) != len(labelValues) {
-		return labels, fmt.Errorf("Length mismatch: len(labelKeys)=%d len(labelValues)=%d", len(labelKeys), len(labelValues))
+		return labels, fmt.Errorf("length mismatch: len(labelKeys)=%d len(labelValues)=%d", len(labelKeys), len(labelValues))
 	}
 
 	for i, labelKey := range labelKeys {
diff --git a/monitoredresource/aws_identity_doc_utils.go b/monitoredresource/aws_identity_doc_utils.go
index d6a23a8..ee519a4 100644
--- a/monitoredresource/aws_identity_doc_utils.go
+++ b/monitoredresource/aws_identity_doc_utils.go
@@ -37,8 +37,12 @@ type awsIdentityDocument struct {
 // This is only done once.
 func retrieveAWSIdentityDocument() *awsIdentityDocument {
 	awsIdentityDoc := awsIdentityDocument{}
-	c := ec2metadata.New(session.New())
-	if c.Available() == false {
+	sesion, err := session.NewSession()
+	if err != nil {
+		return nil
+	}
+	c := ec2metadata.New(sesion)
+	if !c.Available() {
 		return nil
 	}
 	ec2InstanceIdentifyDocument, err := c.GetInstanceIdentityDocument()
diff --git a/monitoredresource/gcp_metadata_config.go b/monitoredresource/gcp_metadata_config.go
index 412e347..f0d8885 100644
--- a/monitoredresource/gcp_metadata_config.go
+++ b/monitoredresource/gcp_metadata_config.go
@@ -22,7 +22,7 @@ import (
 	"strings"
 
 	"cloud.google.com/go/compute/metadata"
-	"cloud.google.com/go/container/apiv1"
+	container "cloud.google.com/go/container/apiv1"
 	containerpb "google.golang.org/genproto/googleapis/container/v1"
 )
 
diff --git a/resource.go b/resource.go
index 430c5ad..e60e913 100644
--- a/resource.go
+++ b/resource.go
@@ -22,13 +22,6 @@ import (
 	monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres"
 )
 
-type resourceMap struct {
-	// Mapping from the input resource type to the monitored resource type in Stackdriver.
-	srcType, dstType string
-	// Mapping from Stackdriver monitored resource label to an OpenCensus resource label.
-	labels map[string]string
-}
-
 // Resource labels that are generally internal to the exporter.
 // Consider exposing these labels and a type identifier in the future to allow
 // for customization.
diff --git a/stackdriver.go b/stackdriver.go
index 68ab4d1..e7d86bc 100644
--- a/stackdriver.go
+++ b/stackdriver.go
@@ -279,10 +279,6 @@ func NewExporter(o Options) (*Exporter, error) {
 	}
 	if o.Location == "" {
 		if metadataapi.OnGCE() {
-			ctx := o.Context
-			if ctx == nil {
-				ctx = context.Background()
-			}
 			zone, err := metadataapi.Zone()
 			if err != nil {
 				// This error should be logged with a warning level.
@@ -425,12 +421,10 @@ func (o Options) handleError(err error) {
 	log.Printf("Failed to export to Stackdriver: %v", err)
 }
 
-func (o Options) newContextWithTimeout() (context.Context, func()) {
-	ctx := o.Context
+func newContextWithTimeout(ctx context.Context, timeout time.Duration) (context.Context, func()) {
 	if ctx == nil {
 		ctx = context.Background()
 	}
-	timeout := o.Timeout
 	if timeout <= 0 {
 		timeout = defaultTimeout
 	}
diff --git a/stats.go b/stats.go
index 72fe486..6b05927 100644
--- a/stats.go
+++ b/stats.go
@@ -212,7 +212,7 @@ func (e *statsExporter) Flush() {
 }
 
 func (e *statsExporter) uploadStats(vds []*view.Data) error {
-	ctx, cancel := e.o.newContextWithTimeout()
+	ctx, cancel := newContextWithTimeout(e.o.Context, e.o.Timeout)
 	defer cancel()
 	ctx, span := trace.StartSpan(
 		ctx,
@@ -331,19 +331,6 @@ func (e *statsExporter) viewToMetricDescriptor(ctx context.Context, v *view.View
 	return res, nil
 }
 
-func (e *statsExporter) viewToCreateMetricDescriptorRequest(ctx context.Context, v *view.View) (*monitoringpb.CreateMetricDescriptorRequest, error) {
-	inMD, err := e.viewToMetricDescriptor(ctx, v)
-	if err != nil {
-		return nil, err
-	}
-
-	cmrdesc := &monitoringpb.CreateMetricDescriptorRequest{
-		Name:             fmt.Sprintf("projects/%s", e.o.ProjectID),
-		MetricDescriptor: inMD,
-	}
-	return cmrdesc, nil
-}
-
 // createMeasure creates a MetricDescriptor for the given view data in Stackdriver Monitoring.
 // An error will be returned if there is already a metric descriptor created with the same name
 // but it has a different aggregation or keys.
@@ -431,7 +418,7 @@ func (e *statsExporter) combineTimeSeriesToCreateTimeSeriesRequest(ts []*monitor
 	// While for each nonUniqueTimeSeries, we have
 	// to make a unique CreateTimeSeriesRequest.
 	ctsreql = append(ctsreql, &monitoringpb.CreateTimeSeriesRequest{
-		Name:       monitoring.MetricProjectPath(e.o.ProjectID),
+		Name:       fmt.Sprintf("projects/%s", e.o.ProjectID),
 		TimeSeries: uniqueTimeSeries,
 	})
 
diff --git a/trace.go b/trace.go
index 71e7f36..ee6535e 100644
--- a/trace.go
+++ b/trace.go
@@ -121,7 +121,7 @@ func (e *traceExporter) uploadSpans(spans []*tracepb.Span) {
 		Spans: spans,
 	}
 	// Create a never-sampled span to prevent traces associated with exporter.
-	ctx, cancel := e.o.newContextWithTimeout()
+	ctx, cancel := newContextWithTimeout(e.o.Context, e.o.Timeout)
 	defer cancel()
 	ctx, span := trace.StartSpan(
 		ctx,


Test patch:
diff --git a/example_test.go b/example_test.go
index d8e581d..11e49e5 100644
--- a/example_test.go
+++ b/example_test.go
@@ -41,8 +41,8 @@ func Example_defaults() {
 
 	// Subscribe views to see stats in Stackdriver Monitoring.
 	if err := view.Register(
-		ochttp.ClientLatencyView,
-		ochttp.ClientResponseBytesView,
+		ochttp.ClientRoundtripLatencyDistribution,
+		ochttp.ClientReceivedBytesDistribution,
 	); err != nil {
 		log.Fatal(err)
 	}
diff --git a/metrics_proto_test.go b/metrics_proto_test.go
index ce87ad1..2960fc9 100644
--- a/metrics_proto_test.go
+++ b/metrics_proto_test.go
@@ -16,10 +16,10 @@ package stackdriver
 
 import (
 	"context"
+	"fmt"
 	"strings"
 	"testing"
 
-	monitoring "cloud.google.com/go/monitoring/apiv3"
 	resourcepb "github.com/census-instrumentation/opencensus-proto/gen-go/resource/v1"
 	"github.com/golang/protobuf/ptypes/timestamp"
 	distributionpb "google.golang.org/genproto/googleapis/api/distribution"
@@ -636,7 +636,7 @@ func TestCombineTimeSeriesAndDeduplication(t *testing.T) {
 			},
 			want: []*monitoringpb.CreateTimeSeriesRequest{
 				{
-					Name: monitoring.MetricProjectPath(se.o.ProjectID),
+					Name: fmt.Sprintf("projects/%s", se.o.ProjectID),
 					TimeSeries: []*monitoringpb.TimeSeries{
 						{
 							Metric: &googlemetricpb.Metric{
@@ -667,7 +667,7 @@ func TestCombineTimeSeriesAndDeduplication(t *testing.T) {
 					},
 				},
 				{
-					Name: monitoring.MetricProjectPath(se.o.ProjectID),
+					Name: fmt.Sprintf("projects/%s", se.o.ProjectID),
 					TimeSeries: []*monitoringpb.TimeSeries{
 						{
 							Metric: &googlemetricpb.Metric{
diff --git a/scripts/check-test-files.sh b/scripts/check-test-files.sh
new file mode 100755
index 0000000..065513e
--- /dev/null
+++ b/scripts/check-test-files.sh
@@ -0,0 +1,40 @@
+
+#!/bin/bash
+
+set -euo pipefail
+
+COLOR_FIXME=$(printf "\033[31mFIXME\033[0m")
+
+NO_TEST_FILE_DIRS=""
+for dir in $*; do
+  mainFile=$(find ${dir} -maxdepth 1 -name 'main.go')
+  testFiles=$(find ${dir} -maxdepth 1 -name '*_test.go')
+  if [ -z "${testFiles}" ]; then
+    if [ -n "${mainFile}" ]; then
+      continue # single main does not require tests
+    fi
+    if [ -e ${dir}/.nocover ]; then
+      reason=$(cat ${dir}/.nocover)
+      if [ "${reason}" == "" ]; then 
+        echo "error: ${dir}/.nocover must specify reason" >&2
+        exit 1
+      fi
+      echo "Package excluded from coverage: ${dir}"
+      echo "  reason: ${reason}" | sed "/FIXME/s//${COLOR_FIXME}/"
+      continue
+    fi
+    if [ -z "${NO_TEST_FILE_DIRS}" ]; then
+      NO_TEST_FILE_DIRS="${dir}"
+    else
+      NO_TEST_FILE_DIRS="${NO_TEST_FILE_DIRS} ${dir}"
+    fi
+  fi
+done
+
+if [ -n "${NO_TEST_FILE_DIRS}" ]; then
+  echo "*** directories without *_test.go files:" >&2
+  echo ${NO_TEST_FILE_DIRS} | tr ' ' '\n' >&2
+  echo "error: at least one *_test.go file must be in all directories with go files so that they are counted for code coverage" >&2
+  echo "       if no tests are possible for a package (e.g. it only defines types), create empty_test.go" >&2
+  exit 1
+fi
\ No newline at end of file
diff --git a/stats_test.go b/stats_test.go
index 9596262..c43fc3e 100644
--- a/stats_test.go
+++ b/stats_test.go
@@ -20,9 +20,9 @@ import (
 	"testing"
 	"time"
 
+	monitoring "cloud.google.com/go/monitoring/apiv3"
 	"contrib.go.opencensus.io/exporter/stackdriver/monitoredresource"
 
-	"cloud.google.com/go/monitoring/apiv3"
 	"github.com/golang/protobuf/ptypes/timestamp"
 	"github.com/google/go-cmp/cmp"
 	"go.opencensus.io/stats"
@@ -107,7 +107,7 @@ func TestExporter_makeReq(t *testing.T) {
 			vd:     newTestViewData(v, start, end, count1, count2),
 			want: []*monitoringpb.CreateTimeSeriesRequest{
 				{
-					Name: monitoring.MetricProjectPath("proj-id"),
+					Name: fmt.Sprintf("projects/%s", "proj-id"),
 					TimeSeries: []*monitoringpb.TimeSeries{
 						{
 							Metric: &metricpb.Metric{
@@ -182,7 +182,7 @@ func TestExporter_makeReq(t *testing.T) {
 			},
 			want: []*monitoringpb.CreateTimeSeriesRequest{
 				{
-					Name: monitoring.MetricProjectPath("proj-id"),
+					Name: fmt.Sprintf("projects/%s", "proj-id"),
 					TimeSeries: []*monitoringpb.TimeSeries{
 						{
 							Metric: &metricpb.Metric{
@@ -252,7 +252,7 @@ func TestExporter_makeReq(t *testing.T) {
 			vd:     newTestViewData(v, start, end, sum1, sum2),
 			want: []*monitoringpb.CreateTimeSeriesRequest{
 				{
-					Name: monitoring.MetricProjectPath("proj-id"),
+					Name: fmt.Sprintf("projects/%s", "proj-id"),
 					TimeSeries: []*monitoringpb.TimeSeries{
 						{
 							Metric: &metricpb.Metric{
@@ -322,7 +322,7 @@ func TestExporter_makeReq(t *testing.T) {
 			vd:     newTestViewData(lastValueView, start, end, &last1, &last2),
 			want: []*monitoringpb.CreateTimeSeriesRequest{
 				{
-					Name: monitoring.MetricProjectPath("proj-id"),
+					Name: fmt.Sprintf("projects/%s", "proj-id"),
 					TimeSeries: []*monitoringpb.TimeSeries{
 						{
 							Metric: &metricpb.Metric{
@@ -383,7 +383,7 @@ func TestExporter_makeReq(t *testing.T) {
 			projID: "proj-id",
 			vd:     newTestDistViewData(distView, start, end),
 			want: []*monitoringpb.CreateTimeSeriesRequest{{
-				Name: monitoring.MetricProjectPath("proj-id"),
+				Name: fmt.Sprintf("projects/%s", "proj-id"),
 				TimeSeries: []*monitoringpb.TimeSeries{
 					{
 						Metric: &metricpb.Metric{
@@ -429,7 +429,7 @@ func TestExporter_makeReq(t *testing.T) {
 			projID: "proj-id",
 			vd:     newTestDistViewData(distView, start, end),
 			want: []*monitoringpb.CreateTimeSeriesRequest{{
-				Name: monitoring.MetricProjectPath("proj-id"),
+				Name: fmt.Sprintf("projects/%s", "proj-id"),
 				TimeSeries: []*monitoringpb.TimeSeries{
 					{
 						Metric: &metricpb.Metric{
@@ -947,7 +947,7 @@ func TestExporter_makeReq_withCustomMonitoredResource(t *testing.T) {
 			vd:   newTestViewData(v, start, end, count1, count2),
 			want: []*monitoringpb.CreateTimeSeriesRequest{
 				{
-					Name: monitoring.MetricProjectPath("proj-id"),
+					Name: fmt.Sprintf("projects/%s", "proj-id"),
 					TimeSeries: []*monitoringpb.TimeSeries{
 						{
 							Metric: &metricpb.Metric{
@@ -1020,7 +1020,7 @@ func TestExporter_makeReq_withCustomMonitoredResource(t *testing.T) {
 			vd: newTestViewData(v, start, end, count1, count2),
 			want: []*monitoringpb.CreateTimeSeriesRequest{
 				{
-					Name: monitoring.MetricProjectPath("proj-id"),
+					Name: fmt.Sprintf("projects/%s", "proj-id"),
 					TimeSeries: []*monitoringpb.TimeSeries{
 						{
 							Metric: &metricpb.Metric{
@@ -1093,7 +1093,7 @@ func TestExporter_makeReq_withCustomMonitoredResource(t *testing.T) {
 			vd: newTestViewData(v, start, end, count1, count2),
 			want: []*monitoringpb.CreateTimeSeriesRequest{
 				{
-					Name: monitoring.MetricProjectPath("proj-id"),
+					Name: fmt.Sprintf("projects/%s", "proj-id"),
 					TimeSeries: []*monitoringpb.TimeSeries{
 						{
 							Metric: &metricpb.Metric{
@@ -1159,7 +1159,7 @@ func TestExporter_makeReq_withCustomMonitoredResource(t *testing.T) {
 			vd:   newTestViewData(v, start, end, count1, count2),
 			want: []*monitoringpb.CreateTimeSeriesRequest{
 				{
-					Name: monitoring.MetricProjectPath("proj-id"),
+					Name: fmt.Sprintf("projects/%s", "proj-id"),
 					TimeSeries: []*monitoringpb.TimeSeries{
 						{
 							Metric: &metricpb.Metric{
@@ -1337,21 +1337,3 @@ func newTestDistViewData(v *view.View, start, end time.Time) *view.Data {
 		End:   end,
 	}
 }
-
-func newTestDistViewDataWithZeroBucket(v *view.View, start, end time.Time) *view.Data {
-	return &view.Data{
-		View: v,
-		Rows: []*view.Row{
-			{Data: &view.DistributionData{
-				Count:           5,
-				Min:             1,
-				Max:             7,
-				Mean:            3,
-				SumOfSquaredDev: 1.5,
-				CountPerBucket:  []int64{0, 2, 2, 1},
-			}},
-		},
-		Start: start,
-		End:   end,
-	}
-}
diff --git a/trace_proto_test.go b/trace_proto_test.go
index bdf29f8..f09d3cf 100644
--- a/trace_proto_test.go
+++ b/trace_proto_test.go
@@ -34,8 +34,6 @@ import (
 	statuspb "google.golang.org/genproto/googleapis/rpc/status"
 )
 
-const projectID = "testproject"
-
 var (
 	traceID = trace.TraceID{0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f}
 	spanID  = trace.SpanID{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}
diff --git a/trace_test.go b/trace_test.go
index b96e632..a8f5bab 100644
--- a/trace_test.go
+++ b/trace_test.go
@@ -67,7 +67,7 @@ func TestNewContext_Timeout(t *testing.T) {
 	e := newTraceExporterWithClient(Options{
 		Timeout: 10 * time.Millisecond,
 	}, nil)
-	ctx, cancel := e.o.newContextWithTimeout()
+	ctx, cancel := newContextWithTimeout(e.o.Context, e.o.Timeout)
 	defer cancel()
 	select {
 	case <-time.After(60 * time.Second):