github.com/bazelbuild/rules_webtesting@v0.2.0/go/metadata/metadata.go (about)

     1  // Copyright 2016 Google Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package metadata provides a struct for storing browser metadata.
    16  package metadata
    17  
    18  import (
    19  	"encoding/json"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  
    24  	"github.com/bazelbuild/rules_webtesting/go/metadata/capabilities"
    25  )
    26  
    27  // Values for Metadata.RecordVideo.
    28  const (
    29  	RecordNever  = "never"
    30  	RecordFailed = "failed"
    31  	RecordAlways = "always"
    32  )
    33  
    34  // Metadata provides necessary metadata for launching a browser.
    35  type Metadata struct {
    36  	// The Capabilities that should be used for this browser.
    37  	Capabilities map[string]interface{} `json:"capabilities,omitempty"`
    38  	// The Environment that web test launcher should use to to launch the browser.
    39  	Environment string `json:"environment,omitempty"`
    40  	// Label for the web_test rule.
    41  	Label string `json:"label,omitempty"`
    42  	// Browser label set in the web_test rule.
    43  	BrowserLabel string `json:"browserLabel,omitempty"`
    44  	// Test label set in the web_test rule.
    45  	TestLabel string `json:"testLabel,omitempty"`
    46  	// Config label set in the web_test rule.
    47  	ConfigLabel string `json:"configLabel,omitempty"`
    48  	// Port to connect debugger to. If 0, debugger will not be started.
    49  	DebuggerPort int `json:"debuggerPort,omitempty"`
    50  	// A list of WebTestFiles with named files in them.
    51  	WebTestFiles []*WebTestFiles `json:"webTestFiles,omitempty"`
    52  	// An object for any additional metadata fields on this object.
    53  	Extension `json:"extension,omitempty"`
    54  }
    55  
    56  // Extension is an interface for adding additional fields that will be parsed as part of the metadata.
    57  type Extension interface {
    58  	// Merge merges this extension data with another set of Extension data. It should not mutate either
    59  	// Extension object, but it is allowed to return one of the Extension objects unchanged if needed.
    60  	// In general values in other should take precedence over values in this object.
    61  	Merge(other Extension) (Extension, error)
    62  	// Normalize normalizes and validate the extension data.
    63  	Normalize() error
    64  }
    65  
    66  // Merge takes two Metadata objects and merges them into a new Metadata object.
    67  func Merge(m1, m2 *Metadata) (*Metadata, error) {
    68  	capabilities := capabilities.Merge(m1.Capabilities, m2.Capabilities)
    69  
    70  	environment := m1.Environment
    71  	if m2.Environment != "" {
    72  		environment = m2.Environment
    73  	}
    74  
    75  	label := m1.Label
    76  	if m2.Label != "" {
    77  		label = m2.Label
    78  	}
    79  
    80  	browserLabel := m1.BrowserLabel
    81  	if m2.BrowserLabel != "" {
    82  		browserLabel = m2.BrowserLabel
    83  	}
    84  
    85  	testLabel := m1.TestLabel
    86  	if m2.TestLabel != "" {
    87  		testLabel = m2.TestLabel
    88  	}
    89  
    90  	configLabel := m1.ConfigLabel
    91  	if m2.ConfigLabel != "" {
    92  		configLabel = m2.ConfigLabel
    93  	}
    94  
    95  	debuggerPort := m1.DebuggerPort
    96  	if m2.DebuggerPort != 0 {
    97  		debuggerPort = m2.DebuggerPort
    98  	}
    99  
   100  	var webTestFiles []*WebTestFiles
   101  	webTestFiles = append(webTestFiles, m1.WebTestFiles...)
   102  	webTestFiles = append(webTestFiles, m2.WebTestFiles...)
   103  
   104  	webTestFiles, err := normalizeWebTestFiles(webTestFiles)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	extension := m1.Extension
   110  	if extension == nil {
   111  		extension = m2.Extension
   112  	} else if m2.Extension != nil {
   113  		e, err := extension.Merge(m2.Extension)
   114  		if err != nil {
   115  			return nil, err
   116  		}
   117  		extension = e
   118  	}
   119  
   120  	return &Metadata{
   121  		Capabilities: capabilities,
   122  		Environment:  environment,
   123  		Label:        label,
   124  		BrowserLabel: browserLabel,
   125  		TestLabel:    testLabel,
   126  		ConfigLabel:  configLabel,
   127  		DebuggerPort: debuggerPort,
   128  		WebTestFiles: webTestFiles,
   129  		Extension:    extension,
   130  	}, nil
   131  }
   132  
   133  // FromFile reads a Metadata object from a json file.
   134  func FromFile(filename string, ext Extension) (*Metadata, error) {
   135  	bytes, err := ioutil.ReadFile(filename)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  
   140  	return FromBytes(bytes, ext)
   141  }
   142  
   143  // FromBytes reads a Metadata object from a byte array.
   144  func FromBytes(bytes []byte, ext Extension) (*Metadata, error) {
   145  	if ext == nil {
   146  		ext = &extension{}
   147  	}
   148  	metadata := &Metadata{Extension: ext}
   149  
   150  	if err := json.Unmarshal(bytes, metadata); err != nil {
   151  		return nil, err
   152  	}
   153  	webTestFiles, err := normalizeWebTestFiles(metadata.WebTestFiles)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	metadata.WebTestFiles = webTestFiles
   158  
   159  	if metadata.Extension != nil {
   160  		if err := metadata.Extension.Normalize(); err != nil {
   161  			return nil, err
   162  		}
   163  	}
   164  
   165  	return metadata, nil
   166  }
   167  
   168  // ToFile writes m to filename as json.
   169  func (m *Metadata) ToFile(filename string) error {
   170  	bytes, err := m.ToBytes()
   171  	if err != nil {
   172  		return err
   173  	}
   174  	return ioutil.WriteFile(filename, bytes, 0644)
   175  }
   176  
   177  // ToBytes serializes metadata.
   178  func (m *Metadata) ToBytes() ([]byte, error) {
   179  	return json.MarshalIndent(m, "", "  ")
   180  }
   181  
   182  // GetFilePath returns the path to a file specified by web_test_archive,
   183  // web_test_named_executable, or web_test_named_file.
   184  func (m *Metadata) GetFilePath(name string) (string, error) {
   185  	for _, a := range m.WebTestFiles {
   186  		filename, err := a.getFilePath(name, m)
   187  		if err != nil {
   188  			return "", err
   189  		}
   190  		if filename != "" {
   191  			return filename, nil
   192  		}
   193  	}
   194  	return "", fmt.Errorf("no named file %q", name)
   195  }
   196  
   197  // Resolver returns a Resolver that processes ENV, FILE, and METADATA prefixed
   198  // capabilities variables.
   199  func (m *Metadata) Resolver() capabilities.Resolver {
   200  	metadataResolver := capabilities.MapResolver("METADATA", map[string]string{
   201  		"LABEL":         m.Label,
   202  		"TEST_LABEL":    m.TestLabel,
   203  		"BROWSER_LABEL": m.BrowserLabel,
   204  		"CONFIG_LABEL":  m.ConfigLabel,
   205  		"ENVIRONMENT":   m.Environment,
   206  	})
   207  
   208  	return func(prefix, name string) (string, error) {
   209  		switch prefix {
   210  		case "ENV":
   211  			v, ok := os.LookupEnv(name)
   212  			if !ok {
   213  				return "", fmt.Errorf("environment variable %q is not defined", name)
   214  			}
   215  			return v, nil
   216  		case "FILE":
   217  			return m.GetFilePath(name)
   218  		default:
   219  			return metadataResolver(prefix, name)
   220  		}
   221  	}
   222  }
   223  
   224  type extension map[string]interface{}
   225  
   226  func (e extension) Merge(other Extension) (Extension, error) {
   227  	if other == nil {
   228  		return e, nil
   229  	}
   230  	if e == nil || len(e) == 0 {
   231  		return other, nil
   232  	}
   233  	o, ok := other.(extension)
   234  	if !ok || len(o) == 0 {
   235  		return e, nil
   236  	}
   237  	ext := extension{}
   238  	for k, v := range e {
   239  		ext[k] = v
   240  	}
   241  	for k, v := range o {
   242  		ext[k] = v
   243  	}
   244  	return ext, nil
   245  }
   246  
   247  func (e extension) Normalize() error {
   248  	return nil
   249  }