Skip to content

Commit

Permalink
Add dependency graph unit tests template, and glClear() example (#2745)
Browse files Browse the repository at this point in the history
The example demonstrate how to build small captures programmatically
and to check it for both expected and unexpected dependencies.

The API file changes introduce dependencies between glClear() and
glClearColor() / glClearDepthf() / glClearStencil().
  • Loading branch information
hevrard authored May 14, 2019
1 parent 5bcf385 commit ce76367
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 0 deletions.
2 changes: 2 additions & 0 deletions core/assert/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ go_library(
name = "go_default_library",
srcs = [
"assertion.go",
"boolean.go",
"enum.go",
"error.go",
"float.go",
Expand All @@ -42,6 +43,7 @@ go_test(
name = "go_default_test",
size = "small",
srcs = [
"boolean_test.go",
"enum_test.go",
"error_test.go",
"float_test.go",
Expand Down
47 changes: 47 additions & 0 deletions core/assert/boolean.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (C) 2019 Google Inc.
//
// 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 assert

// OnBoolean is the result of calling ThatBoolean on an Assertion.
// It provides boolean assertion tests.
type OnBoolean struct {
Assertion
value bool
}

// ThatBoolean returns an OnBoolean for boolean based assertions.
func (a Assertion) ThatBoolean(value bool) OnBoolean {
return OnBoolean{Assertion: a, value: value}
}

// Equals asserts that the supplied boolean is equal to the expected boolean.
func (o OnBoolean) Equals(expect bool) bool {
return o.Compare(o.value, "==", expect).Test(o.value == expect)
}

// NotEquals asserts that the supplied boolean is not equal to the test boolean.
func (o OnBoolean) NotEquals(test bool) bool {
return o.Compare(o.value, "!=", test).Test(o.value != test)
}

// IsTrue asserts that the supplied boolean is true
func (o OnBoolean) IsTrue() bool {
return o.Equals(true)
}

// IsFalse asserts that the supplied boolean is false
func (o OnBoolean) IsFalse() bool {
return o.Equals(false)
}
26 changes: 26 additions & 0 deletions core/assert/boolean_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (C) 2019 Google Inc.
//
// 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 assert_test

import "github.com/google/gapid/core/assert"

// An example of testing integer values
func ExampleBoolean() {
assert := assert.To(nil)
assert.For("true Equals true").ThatBoolean(true).Equals(true)
assert.For("true NotEquals false").ThatBoolean(true).NotEquals(false)
assert.For("true IsTrue").ThatBoolean(true).IsTrue()
assert.For("false IsFalse").ThatBoolean(false).IsFalse()
}
2 changes: 2 additions & 0 deletions gapis/api/gles/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ go_test(
srcs = [
"compat_test.go",
"dead_code_elimination_test.go",
"dependencygraph2_test.go",
"markers_test.go",
"stub_program_test.go",
],
Expand All @@ -188,5 +189,6 @@ go_test(
"//gapis/database:go_default_library",
"//gapis/memory:go_default_library",
"//gapis/resolve/dependencygraph:go_default_library",
"//gapis/resolve/dependencygraph2:go_default_library",
],
)
3 changes: 3 additions & 0 deletions gapis/api/gles/api/framebuffer.api
Original file line number Diff line number Diff line change
Expand Up @@ -296,16 +296,19 @@ cmd void glClear(GLbitfield mask) {
fb := ctx.Bound.DrawFramebuffer

if (GL_COLOR_BUFFER_BIT in mask) {
_ = ctx.Pixel.ColorClearValue
for _, _, a in fb.ColorAttachments {
WriteGPUFramebufferAttachment(a)
}
}

if (GL_DEPTH_BUFFER_BIT in mask) {
_ = ctx.Pixel.DepthClearValue
WriteGPUFramebufferAttachment(fb.DepthAttachment)
}

if (GL_STENCIL_BUFFER_BIT in mask) {
_ = ctx.Pixel.StencilClearValue
WriteGPUFramebufferAttachment(fb.StencilAttachment)
}

Expand Down
219 changes: 219 additions & 0 deletions gapis/api/gles/dependencygraph2_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// Copyright (C) 2019 Google Inc.
//
// 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 gles_test

import (
"context"
"fmt"
"testing"

"github.com/google/gapid/core/assert"
"github.com/google/gapid/core/log"
"github.com/google/gapid/core/memory/arena"
"github.com/google/gapid/core/os/device"
"github.com/google/gapid/core/os/device/bind"
"github.com/google/gapid/gapis/api"
"github.com/google/gapid/gapis/api/gles"
"github.com/google/gapid/gapis/capture"
"github.com/google/gapid/gapis/database"
"github.com/google/gapid/gapis/memory"
"github.com/google/gapid/gapis/resolve/dependencygraph2"
)

// Test helpers

// testDependencyCmdName is used as a map key to easily express
// expected dependencies
type testDependencyCmdName struct {
sourceCmdName, targetCmdName string
}

// getCommandName return the command name of a graph node
func getCommandName(graph dependencygraph2.DependencyGraph, nodeId dependencygraph2.NodeID) string {
node := graph.GetNode(nodeId)
if cmdNode, ok := node.(dependencygraph2.CmdNode); ok {
cmdId := cmdNode.Index[0]
if api.CmdID(cmdId).IsReal() {
command := graph.GetCommand(api.CmdID(cmdId))
return command.CmdName()
}
}
return ""
}

// testCaptureAndGraph abstracts the boilerplate for creating a capture
// programmatically, check expected dependencies in its graph, and
// clean out
type testCaptureAndGraph struct {
ctx context.Context
arena arena.Arena
cb gles.CommandBuilder
cmds []api.Cmd
}

// init runs the boilerplate up to being able to add commands to tc.cmds
func (tc *testCaptureAndGraph) init(t *testing.T) {
tc.ctx = log.Testing(t)
tc.ctx = bind.PutRegistry(tc.ctx, bind.NewRegistry())
tc.ctx = database.Put(tc.ctx, database.NewInMemory(tc.ctx))
tc.arena = arena.New()
ctxHandle := memory.BytePtr(1)
displayHandle := memory.BytePtr(2)
surfaceHandle := memory.BytePtr(3)
tc.cb = gles.CommandBuilder{Thread: 0, Arena: tc.arena}

// Common prologue: make an EGL context
tc.cmds = []api.Cmd{
tc.cb.EglCreateContext(displayHandle, surfaceHandle, surfaceHandle, memory.Nullptr, ctxHandle),
api.WithExtras(
tc.cb.EglMakeCurrent(displayHandle, surfaceHandle, surfaceHandle, ctxHandle, 0),
gles.NewStaticContextStateForTest(tc.arena), gles.NewDynamicContextStateForTest(tc.arena, 64, 64, false)),
}
}

// terminate cleans up what need to be
func (tc *testCaptureAndGraph) terminate() {
tc.arena.Dispose()
}

// checkDependenciesArePresent generate the graph and check if
// expected dependencies present
func (tc *testCaptureAndGraph) checkDependenciesArePresent(t *testing.T, expected, unexpected []testDependencyCmdName) {
// Compute dependency graph
header := &capture.Header{ABI: device.AndroidARM64v8a}
cap, err := capture.NewGraphicsCapture(tc.ctx, tc.arena, t.Name(), header, nil, tc.cmds)
if err != nil {
panic(err)
}
capturePath, err := cap.Path(tc.ctx)
if err != nil {
panic(err)
}
tc.ctx = capture.Put(tc.ctx, capturePath)

cfg := dependencygraph2.DependencyGraphConfig{
MergeSubCmdNodes: true,
IncludeInitialCommands: false,
}
graph, err := dependencygraph2.GetDependencyGraph(tc.ctx, capturePath, cfg)
if err != nil {
panic(err)
}

// Check dependencies
mapExpected := map[testDependencyCmdName]bool{}
for _, dep := range expected {
mapExpected[dep] = false
}
mapUnexpected := map[testDependencyCmdName]bool{}
for _, dep := range unexpected {
mapUnexpected[dep] = false
}

graph.ForeachDependency(
func(src, tgt dependencygraph2.NodeID) error {
srcCmdName := getCommandName(graph, src)
tgtCmdName := getCommandName(graph, tgt)
dep := testDependencyCmdName{srcCmdName, tgtCmdName}
if _, ok := mapExpected[dep]; ok {
mapExpected[dep] = true
}
if _, ok := mapUnexpected[dep]; ok {
mapUnexpected[dep] = true
}
return nil
})

for dep, present := range mapExpected {
assert.For(tc.ctx, fmt.Sprintf("Dependency: %v", dep)).ThatBoolean(present).IsTrue()
}
for dep, present := range mapUnexpected {
assert.For(tc.ctx, fmt.Sprintf("Dependency: %v", dep)).ThatBoolean(present).IsFalse()
}
}

// Actual tests

// glClear(GL_COLOR_BUFFER_BIT) depends on glClearColor()
func TestDependencyGlClearColor(t *testing.T) {
var tc testCaptureAndGraph
tc.init(t)
defer tc.terminate()

tc.cmds = append(tc.cmds,
tc.cb.GlClearColor(1, 1, 1, 1),
tc.cb.GlClearDepthf(1),
tc.cb.GlClearStencil(1),
tc.cb.GlClear(gles.GLbitfield_GL_COLOR_BUFFER_BIT))

expected := []testDependencyCmdName{
testDependencyCmdName{"glClear", "glClearColor"},
}

unexpected := []testDependencyCmdName{
testDependencyCmdName{"glClear", "glClearDepthf"},
testDependencyCmdName{"glClear", "glClearStencil"},
}

tc.checkDependenciesArePresent(t, expected, unexpected)
}

// glClear(GL_DEPTH_BUFFER_BIT) depends on glClearDepthf()
func TestDependencyGlClearDepthf(t *testing.T) {
var tc testCaptureAndGraph
tc.init(t)
defer tc.terminate()

tc.cmds = append(tc.cmds,
tc.cb.GlClearColor(1, 1, 1, 1),
tc.cb.GlClearDepthf(1),
tc.cb.GlClearStencil(1),
tc.cb.GlClear(gles.GLbitfield_GL_DEPTH_BUFFER_BIT))

expected := []testDependencyCmdName{
testDependencyCmdName{"glClear", "glClearDepthf"},
}

unexpected := []testDependencyCmdName{
testDependencyCmdName{"glClear", "glClearColor"},
testDependencyCmdName{"glClear", "glClearStencil"},
}

tc.checkDependenciesArePresent(t, expected, unexpected)
}

// glClear(GL_STENCIL_BUFFER_BIT) depends on glClearStencil()
func TestDependencyGlClearStencil(t *testing.T) {
var tc testCaptureAndGraph
tc.init(t)
defer tc.terminate()

tc.cmds = append(tc.cmds,
tc.cb.GlClearColor(1, 1, 1, 1),
tc.cb.GlClearDepthf(1),
tc.cb.GlClearStencil(1),
tc.cb.GlClear(gles.GLbitfield_GL_STENCIL_BUFFER_BIT))

expected := []testDependencyCmdName{
testDependencyCmdName{"glClear", "glClearStencil"},
}

unexpected := []testDependencyCmdName{
testDependencyCmdName{"glClear", "glClearColor"},
testDependencyCmdName{"glClear", "glClearDepthf"},
}

tc.checkDependenciesArePresent(t, expected, unexpected)
}

0 comments on commit ce76367

Please sign in to comment.