diff --git a/metrics/Android.bp b/metrics/Android.bp index 3668668..1e3a6f0 100644 --- a/metrics/Android.bp +++ b/metrics/Android.bp @@ -24,4 +24,7 @@ bootstrap_go_package { srcs: [ "event_handler.go", ], + testSrcs: [ + "event_handler_test.go", + ], } diff --git a/metrics/event_handler.go b/metrics/event_handler.go index 3fd0f37..35a6858 100644 --- a/metrics/event_handler.go +++ b/metrics/event_handler.go @@ -56,6 +56,9 @@ func (e Event) RuntimeNanoseconds() uint64 { // call to End (though other events may begin and end before this event ends). // Events within the same scope must have unique names. func (h *EventHandler) Begin(name string) { + if strings.ContainsRune(name, '.') { + panic(fmt.Sprintf("illegal event name (avoid dot): %s", name)) + } h.scopeIds = append(h.scopeIds, name) h.scopeStartTimes = append(h.scopeStartTimes, _now()) } @@ -71,7 +74,7 @@ func (h *EventHandler) Do(name string, f func()) { // themselves been marked completed. func (h *EventHandler) End(name string) { if len(h.scopeIds) == 0 || name != h.scopeIds[len(h.scopeIds)-1] { - panic(fmt.Errorf("Unexpected scope end '%s'. Current scope: (%s)", + panic(fmt.Errorf("unexpected scope end '%s'. Current scope: (%s)", name, h.scopeIds)) } event := Event{ @@ -94,14 +97,14 @@ func (h *EventHandler) End(name string) { func (h *EventHandler) CompletedEvents() []Event { if len(h.scopeIds) > 0 { panic(fmt.Errorf( - "Retrieving events before all events have been closed. Current scope: (%s)", + "retrieving events before all events have been closed. Current scope: (%s)", h.scopeIds)) } // Validate no two events have the same full id. ids := map[string]struct{}{} for _, event := range h.completedEvents { if _, containsId := ids[event.Id]; containsId { - panic(fmt.Errorf("Duplicate event registered: %s", event.Id)) + panic(fmt.Errorf("duplicate event registered: %s", event.Id)) } ids[event.Id] = struct{}{} } diff --git a/metrics/event_handler_test.go b/metrics/event_handler_test.go new file mode 100644 index 0000000..08a59bd --- /dev/null +++ b/metrics/event_handler_test.go @@ -0,0 +1,100 @@ +// Copyright 2022 Google Inc. All Rights Reserved. +// +// 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. + +package metrics + +import ( + "fmt" + "reflect" + "strings" + "testing" +) + +func Map[A any, B any](in []A, f func(A) B) []B { + r := make([]B, len(in)) + for i, a := range in { + r[i] = f(a) + } + return r +} + +func TestEventNameWithDot(t *testing.T) { + defer func() { + r := fmt.Sprintf("%v", recover()) + if !strings.HasPrefix(r, "illegal event name") { + t.Errorf("The code did not panic in the expected manner: %s", r) + } + }() + eh := EventHandler{} + eh.Begin("a.") +} + +func TestEventNesting(t *testing.T) { + eh := EventHandler{} + eh.Begin("a") + eh.Begin("b") + eh.End("b") + eh.Begin("c") + eh.End("c") + eh.End("a") + expected := []string{"a.b", "a.c", "a"} + actual := Map(eh.CompletedEvents(), func(e Event) string { + return e.Id + }) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected: %s actual %s", expected, actual) + } +} + +func TestEventOverlap(t *testing.T) { + defer func() { + r := fmt.Sprintf("%v", recover()) + if !strings.Contains(r, "unexpected scope end 'a'") { + t.Errorf("expected panic but: %s", r) + } + }() + eh := EventHandler{} + eh.Begin("a") + eh.Begin("b") + eh.End("a") +} + +func TestEventDuplication(t *testing.T) { + eh := EventHandler{} + eh.Begin("a") + eh.Begin("b") + eh.End("b") + eh.Begin("b") + eh.End("b") + eh.End("a") + defer func() { + r := fmt.Sprintf("%v", recover()) + if !strings.HasPrefix(r, "duplicate event") { + t.Errorf("expected panic but: %s", r) + } + }() + eh.CompletedEvents() +} + +func TestIncompleteEvent(t *testing.T) { + eh := EventHandler{} + eh.Begin("a") + defer func() { + r := fmt.Sprintf("%v", recover()) + if !strings.HasPrefix(r, "retrieving events before all events have been closed.") { + t.Errorf("expected panic but: %s", r) + } + }() + eh.CompletedEvents() +}