profiler: add function for checking if filename is in profile

Change-Id: I6eedaf12255fe1b7bcfa7859521aa960781fe5ab
Reviewed-on: https://code-review.googlesource.com/c/35752
Reviewed-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alexey Alexandrov <aalexand@google.com>
diff --git a/profiler/proftest/proftest.go b/profiler/proftest/proftest.go
index b103dd5..1da1098 100644
--- a/profiler/proftest/proftest.go
+++ b/profiler/proftest/proftest.go
@@ -75,12 +75,12 @@
 
 // ProfileData has data of a single profile.
 type ProfileData struct {
-	Samples           []int32       `json:"samples"`
-	SampleMetrics     interface{}   `json:"sampleMetrics"`
-	DefaultMetricType string        `json:"defaultMetricType"`
-	TreeNodes         interface{}   `json:"treeNodes"`
-	Functions         functionArray `json:"functions"`
-	SourceFiles       interface{}   `json:"sourceFiles"`
+	Samples           []int32         `json:"samples"`
+	SampleMetrics     interface{}     `json:"sampleMetrics"`
+	DefaultMetricType string          `json:"defaultMetricType"`
+	TreeNodes         interface{}     `json:"treeNodes"`
+	Functions         functionArray   `json:"functions"`
+	SourceFiles       sourceFileArray `json:"sourceFiles"`
 }
 
 type functionArray struct {
@@ -88,6 +88,10 @@
 	Sourcefile []int32  `json:"sourceFile"`
 }
 
+type sourceFileArray struct {
+	Name []string `json:"name"`
+}
+
 // InstanceConfig is configuration for starting single GCE instance for
 // profiling agent test case.
 type InstanceConfig struct {
@@ -111,20 +115,25 @@
 	Dockerfile      string
 }
 
+// CheckNonEmpty returns nil if the profile has a profiles and deployments
+// associated. Otherwise, returns a desciptive error.
+func (pr *ProfileResponse) CheckNonEmpty() error {
+	if pr.NumProfiles == 0 {
+		return fmt.Errorf("profile response contains zero profiles: %v", pr)
+	}
+	if len(pr.Deployments) == 0 {
+		return fmt.Errorf("profile response contains zero deployments: %v", pr)
+	}
+	return nil
+}
+
 // HasFunction returns nil if the function is present, or, if the function is
 // not present, and error providing more details why the function is not
 // present.
 func (pr *ProfileResponse) HasFunction(functionName string) error {
-	if pr.NumProfiles == 0 {
-		return fmt.Errorf("failed to find function name %s in profile: profile response contains zero profiles: %v", functionName, pr)
+	if err := pr.CheckNonEmpty(); err != nil {
+		return fmt.Errorf("failed to find function name %s in profile: %v", functionName, err)
 	}
-	if len(pr.Deployments) == 0 {
-		return fmt.Errorf("failed to find function name %s in profile: profile response contains zero deployments: %v", functionName, pr)
-	}
-	if len(pr.Profile.Functions.Name) == 0 {
-		return fmt.Errorf("failed to find function name %s in profile: profile does not have function data", functionName)
-	}
-
 	for _, name := range pr.Profile.Functions.Name {
 		if strings.Contains(name, functionName) {
 			return nil
@@ -133,6 +142,21 @@
 	return fmt.Errorf("failed to find function name %s in profile", functionName)
 }
 
+// HasSourceFile returns nil if the file (or file where the end of the file path
+// matches the filename) is present in the profile. Or, if the filename is not
+// present, an error is returned.
+func (pr *ProfileResponse) HasSourceFile(filename string) error {
+	if err := pr.CheckNonEmpty(); err != nil {
+		return fmt.Errorf("failed to find filename %s in profile: %v", filename, err)
+	}
+	for _, name := range pr.Profile.SourceFiles.Name {
+		if strings.HasSuffix(name, filename) {
+			return nil
+		}
+	}
+	return fmt.Errorf("failed to find filename %s in profile", filename)
+}
+
 // StartInstance starts a GCE Instance with name, zone, and projectId specified
 // by the inst, and which runs the startup script specified in inst.
 func (tr *GCETestRunner) StartInstance(ctx context.Context, inst *InstanceConfig) error {