text:
// Copyright 2019 Google LLC
//
// 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
//
//     https://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 transformers_test

import (
	"net/url"
	"strings"
	"testing"

	"github.com/ampproject/amppackager/transformer/internal/amphtml"
	"github.com/ampproject/amppackager/transformer/transformers"
	"golang.org/x/net/html"
	"github.com/kylelemons/godebug/diff"
)

type TestCase = struct {
	testcaseName string
	input        string
	expected     string
}

func transformAndOutput(input string, version int64) (string, error) {
	inputDoc, err := html.Parse(strings.NewReader(input))
	if err != nil {
		return "", err
	}
	inputDOM, err := amphtml.NewDOM(inputDoc)
	if err != nil {
		return "", err
	}
	baseURL, _ := url.Parse("https://www.example.com")
	documentURL, _ := url.Parse("https://www.example.com/foo")

	context := &transformers.Context{
		DOM:         inputDOM,
		BaseURL:     baseURL,
		DocumentURL: documentURL,
		Version:     version,
	}
	transformers.PreloadImage(context)
	var output strings.Builder
	if err := html.Render(&output, inputDoc); err != nil {
		return "", err
	}
	return output.String(), nil
}

func testCases(t *testing.T, cases []TestCase, version int64) {
	for _, tc := range cases {
		t.Run(tc.testcaseName, func(t *testing.T) {
			output, err := transformAndOutput(strings.TrimSpace(tc.input), version)
			if err != nil {
				t.Fatalf("Unexpected error %q", err)
			}
			if diff := diff.Diff(strings.TrimSpace(tc.expected), output); diff != "" {
				t.Errorf("PreloadImage transformer produced unexpected output:\n%s", diff)
			}
		})
	}
}

var testcaseInferSize = []TestCase{
	{
		"inferred-size: Has hero image.",
		`<html><head></head><body><amp-img width="500" height="400" src="https://example.com/foo.png"></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img width="500" height="400" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"inferred-size: Dimensions too small",
		`<html><head></head><body><amp-img height="100" src="https://example.com/foo.png" width="100"></amp-img></body></html>`,
		`<html><head></head><body><amp-img height="100" src="https://example.com/foo.png" width="100"></amp-img></body></html>`,
	},
	{
		"inferred-size: Crossorigin attribute.",
		`<html><head></head><body><amp-img width="500" height="400" src="https://example.com/foo.png" crossorigin="anonymous"></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png" crossorigin="anonymous"/><amp-img width="500" height="400" src="https://example.com/foo.png" crossorigin="anonymous" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"inferred-size: Referrerpolicy attribute.",
		`<html><head></head><body><amp-img width="500" height="400" src="https://example.com/foo.png" referrerpolicy="origin"></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png" referrerpolicy="origin"/><amp-img width="500" height="400" src="https://example.com/foo.png" referrerpolicy="origin" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"inferred-size: Srcset attribute.",
		`<html><head></head><body><amp-img width="500" height="400" src="https://example.com/foo.png" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w"></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w"/><amp-img width="500" height="400" src="https://example.com/foo.png" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"inferred-size: Srcset without src.",
		`<html><head></head><body><amp-img width="500" height="400" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w"></body></html>`,
		`<link rel="preload" as="image" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w"/><amp-img width="500" height="400" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"inferred-size: Sizes",
		`<html><head></head><body><amp-img width="500" height="400" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w" sizes="100vw"></body></html>`,
		`<link rel="preload" as="image" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w" imagesizes="100vw"/><amp-img width="500" height="400" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w" sizes="100vw" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"inferred-size: Several images",
		`<html><head></head><body><amp-img height="100" src="https://example.com/bar.png" width="100"></amp-img><amp-img width="500" height="400" src="https://example.com/foo.png"></amp-img></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img height="100" src="https://example.com/bar.png" width="100"></amp-img><amp-img width="500" height="400" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"inferred-size: Several images none qualifies. (all tiny)",
		`<amp-img height="100" src="https://example-com.cdn.ampproject.org/bar.png" width="100"></amp-img><amp-img width="100" height="100" src="https://example.com/foo.png">`,
		`<amp-img height="100" src="https://example-com.cdn.ampproject.org/bar.png" width="100"></amp-img><amp-img width="100" height="100" src="https://example.com/foo.png"></amp-img>`,
	},
	{
		"inferred-size: Iframe placeholder",
		`<html><head></head><body><amp-iframe height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" src="https://example.com/bar.png"></amp-img></amp-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/bar.png"/><amp-iframe height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" src="https://example.com/bar.png" i-amphtml-ssr=""></amp-img></amp-iframe>`,
	},
	{
		"inferred-size: Iframe placeholder crossorigin",
		`<amp-iframe height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" src="https://example.com/bar.png" crossorigin="anonymous"></amp-img></amp-iframe>`,
		`<link rel="preload" as="image" href="https://example.com/bar.png" crossorigin="anonymous"/><amp-iframe height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" src="https://example.com/bar.png" crossorigin="anonymous" i-amphtml-ssr=""></amp-img></amp-iframe>`,
	},
	{
		"inferred-size: Iframe placeholder referrerpolicy",
		`<amp-iframe height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" src="https://example.com/bar.png" referrerpolicy="origin"></amp-img></amp-iframe>`,
		`<link rel="preload" as="image" href="https://example.com/bar.png" referrerpolicy="origin"/><amp-iframe height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" src="https://example.com/bar.png" referrerpolicy="origin" i-amphtml-ssr=""></amp-img></amp-iframe>`,
	},
	{
		"inferred-size: Iframe placeholder srcset",
		`<amp-iframe height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w"></amp-img></amp-iframe>`,
		`<link rel="preload" as="image" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w"/><amp-iframe height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w" i-amphtml-ssr=""></amp-img></amp-iframe>`,
	},
	{
		"inferred-size: No placeholder image",
		`<html><head></head><body><amp-iframe src="/foo.html"></amp-iframe></body></html>`,
		`<html><head></head><body><amp-iframe src="/foo.html"></amp-iframe></body></html>`,
	},
	{
		"inferred-size: iframe video placeholder",
		`<html><head></head><body><amp-video-iframe height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" src="https://example.com/foo.png"></amp-video-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video-iframe height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img></amp-video-iframe>`,
	},
	{
		"inferred-size: iframe video placeholder crossorigin",
		`<amp-video-iframe height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" src="https://example.com/foo.png" crossorigin="anonymous"></amp-video-iframe>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png" crossorigin="anonymous"/><amp-video-iframe height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" src="https://example.com/foo.png" crossorigin="anonymous" i-amphtml-ssr=""></amp-img></amp-video-iframe>`,
	},
	{
		"inferred-size: iframe video placeholder referrerpolicy",
		`<amp-video-iframe height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" src="https://example.com/foo.png" referrerpolicy="origin"></amp-video-iframe>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png" referrerpolicy="origin"/><amp-video-iframe height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" src="https://example.com/foo.png" referrerpolicy="origin" i-amphtml-ssr=""></amp-img></amp-video-iframe>`,
	},
	{
		"inferred-size: iframe video placeholder srcset",
		`<amp-video-iframe height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w"></amp-img></amp-video-iframe>`,
		`<link rel="preload" as="image" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w"/><amp-video-iframe height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w" i-amphtml-ssr=""></amp-img></amp-video-iframe>`,
	},
	{
		"inferred-size: iframe video poster",
		`<html><head></head><body><amp-video-iframe height="500" width="500" src="/foo.html" poster="https://example.com/foo.png"></amp-video-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video-iframe height="500" width="500" src="/foo.html" poster="https://example.com/foo.png"></amp-video-iframe>`,
	},
	{
		"inferred-size: iframe video poster crossorigin",
		`<html><head></head><body><amp-video-iframe height="500" width="500" src="/foo.html" poster="https://example.com/foo.png" crossorigin="anonymous"></amp-video-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video-iframe height="500" width="500" src="/foo.html" poster="https://example.com/foo.png" crossorigin="anonymous"></amp-video-iframe>`,
	},
	{
		"inferred-size: iframe video poster referrerpolicy",
		`<html><head></head><body><amp-video-iframe height="500" width="500" src="/foo.html" poster="https://example.com/foo.png" referrerpolicy="origin"></amp-video-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video-iframe height="500" width="500" src="/foo.html" poster="https://example.com/foo.png" referrerpolicy="origin"></amp-video-iframe>`,
	},
	{
		"inferred-size: No placeholder image",
		`<html><head></head><body><amp-video-iframe src="/foo.html"></amp-video-iframe></body></html>`,
		`<html><head></head><body><amp-video-iframe src="/foo.html"></amp-video-iframe></body></html>`,
	},
	{
		"inferred-size: Video poster",
		`<html><head></head><body><amp-video poster="https://example.com/foo.png" width="400" height="400"><source src="foo.mp4" /></amp-video></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video poster="https://example.com/foo.png" width="400" height="400"><source src="foo.mp4"/></amp-video>`,
	},
	{
		"inferred-size: Video poster crossorigin",
		`<html><head></head><body><amp-video poster="https://example.com/foo.png" width="400" height="400" crossorigin="anonymous"><source src="foo.mp4" /></amp-video></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video poster="https://example.com/foo.png" width="400" height="400" crossorigin="anonymous"><source src="foo.mp4"/></amp-video>`,
	},
	{
		"inferred-size: Video poster referrerpolicy",
		`<html><head></head><body><amp-video poster="https://example.com/foo.png" width="400" height="400" referrerpolicy="origin"><source src="foo.mp4" /></amp-video></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video poster="https://example.com/foo.png" width="400" height="400" referrerpolicy="origin"><source src="foo.mp4"/></amp-video>`,
	},
	{
		"inferred-size: amp-video with missing poster.",
		`<html><head></head><body><amp-video width="400" height="400"><source src="foo.mp4" /></amp-video></body></html>`,
		`<html><head></head><body><amp-video width="400" height="400"><source src="foo.mp4"/></amp-video></body></html>`,
	},
	{
		"inferred-size: No display layout",
		`<html><head></head><body><amp-img height="500" src="https://example.com/foo.png" width="500" layout="nodisplay"></amp-img></body></html>`,
		`<html><head></head><body><amp-img height="500" src="https://example.com/foo.png" width="500" layout="nodisplay"></amp-img></body></html>`,
	},
	{
		"inferred-size: Same as above with nodisplay layout removed.",
		`<html><head></head><body><amp-img height="500" src="https://example.com/foo.png" width="500"></amp-img></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img height="500" src="https://example.com/foo.png" width="500" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"inferred-size: Invalid protocol",
		`<html><head></head><body><amp-img width="500" height="400" src="ftp://example.com/ftp.png"></body></html>`,
		`<html><head></head><body><amp-img width="500" height="400" src="ftp://example.com/ftp.png"></amp-img></body></html>`,
	},
	{
		"inferred-size: Srcset validity. Empty srcset.",
		`<html><head></head><body><amp-img width="500" height="400" src="https://example.com/foo.png" srcset=""></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img width="500" height="400" src="https://example.com/foo.png" srcset="" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"inferred-size: Invalid srcset",
		`<html><head></head><body><amp-img width="500" height="400" src="https://example.com/foo.png" srcset="foo bar baz"></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img width="500" height="400" src="https://example.com/foo.png" srcset="foo bar baz" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"inferred-size: Invalid srcset duplicates.",
		`<html><head></head><body><amp-img width="500" height="400" src="https://example.com/foo.png" srcset="foo 10w, bar 10w, baz 100w"></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img width="500" height="400" src="https://example.com/foo.png" srcset="foo 10w, bar 10w, baz 100w" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"inferred-size: Hero image dimensions from parent container.",
		`<html><head></head><body><div width="500" height="500"><amp-img layout="fill" src="https://example.com/foo.png"></amp-img></div></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/>
<amp-img layout="fill" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img>
`,
	},
	{
		"inferred-size: Hero image dimesions from parent container, too small.",
		`<html><head></head><body><div width="50" height="50"><amp-img layout="fill" src="https://example.com/foo.png"></amp-img></div></body></html>`,
		`<html><head></head><body><div width="50" height="50"><amp-img layout="fill" src="https://example.com/foo.png"></amp-img></div></body></html>`,
	},
	{
		"inferred-size: No dimensions in parent containers.",
		`<html><head></head><body><div><amp-img layout="fill" src="https://example.com/foo.png"></amp-img></div></body></html>`,
		`<html><head></head><body><div><amp-img layout="fill" src="https://example.com/foo.png"></amp-img></div></body></html>`,
	},
	{
		"inferred-size: No dimension from parent because layout is not responsive or fill",
		`<html><head></head><body><div><amp-img src="https://example.com/foo.png"></amp-img></div></body></html>`,
		`<html><head></head><body><div><amp-img src="https://example.com/foo.png"></amp-img></div></body></html>`,
	},
}

var testcaseDataHeroAmpImg = []TestCase{
	{
		"data-hero",
		`<html><head></head><body><amp-img data-hero width="500" height="400" src="https://example.com/foo.png"></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img data-hero="" width="500" height="400" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"data-hero: Allows multiple heros",
		`<amp-img data-hero width="500" height="400" src="https://example.com/foo.png"></amp-img><amp-img data-hero width="500" height="400" src="https://example.com/bar.png"></amp-img><amp-img data-hero width="500" height="400" src="https://example.com/baz.png"></amp-img>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><link rel="preload" as="image" href="https://example.com/bar.png"/><amp-img data-hero="" width="500" height="400" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img><amp-img data-hero="" width="500" height="400" src="https://example.com/bar.png" i-amphtml-ssr=""></amp-img><amp-img data-hero="" width="500" height="400" src="https://example.com/baz.png"></amp-img>`,
	},
	{
		"data-hero: Prioritizes data-hero",
		`<amp-img width="100" height="100" src="https://example.com/foo.png"></amp-img><amp-img data-hero width="500" height="400" src="https://example.com/bar.png"></amp-img>`,
		`<link rel="preload" as="image" href="https://example.com/bar.png"/><amp-img width="100" height="100" src="https://example.com/foo.png"></amp-img><amp-img data-hero="" width="500" height="400" src="https://example.com/bar.png" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"data-hero: Prevents size-inferred hero",
		`<amp-img data-hero width="500" height="400" src="https://example.com/foo.png"></amp-img><amp-img width="500" height="400" src="https://example.com/bar.png"></amp-img>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img data-hero="" width="500" height="400" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img><amp-img width="500" height="400" src="https://example.com/bar.png"></amp-img>`,
	},
	{
		"data-hero: Dimensions too small",
		`<html><head></head><body><amp-img data-hero height="100" src="https://example.com/foo.png" width="100"></amp-img></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img data-hero="" height="100" src="https://example.com/foo.png" width="100" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"data-hero: Crossorigin attribute.",
		`<html><head></head><body><amp-img data-hero width="500" height="400" src="https://example.com/foo.png" crossorigin="anonymous"></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png" crossorigin="anonymous"/><amp-img data-hero="" width="500" height="400" src="https://example.com/foo.png" crossorigin="anonymous" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"data-hero: Referrerpolicy referrerpolicy.",
		`<html><head></head><body><amp-img data-hero width="500" height="400" src="https://example.com/foo.png" referrerpolicy="origin"></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png" referrerpolicy="origin"/><amp-img data-hero="" width="500" height="400" src="https://example.com/foo.png" referrerpolicy="origin" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"data-hero: Srcset attribute.",
		`<amp-img data-hero width="500" height="400" src="https://example.com/foo.png" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w">`,
		`<link rel="preload" as="image" href="https://example.com/foo.png" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w"/><amp-img data-hero="" width="500" height="400" src="https://example.com/foo.png" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"inferred-size: Srcset without src.",
		`<html><head></head><body><amp-img data-hero width="500" height="400" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w"></body></html>`,
		`<link rel="preload" as="image" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w"/><amp-img data-hero="" width="500" height="400" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"inferred-size: Sizes.",
		`<html><head></head><body><amp-img data-hero width="500" height="400" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w" sizes="100vw"></body></html>`,
		`<link rel="preload" as="image" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w" imagesizes="100vw"/><amp-img data-hero="" width="500" height="400" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w" sizes="100vw" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"data-hero: Iframe placeholder",
		`<html><head></head><body><amp-iframe data-hero height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" src="https://example.com/bar.png"></amp-img></amp-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/bar.png"/><amp-iframe data-hero="" height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" src="https://example.com/bar.png" i-amphtml-ssr=""></amp-img></amp-iframe>`,
	},
	{
		"data-hero: Iframe placeholder crossorigin",
		`<amp-iframe data-hero height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" src="https://example.com/bar.png" crossorigin="anonymous"></amp-img></amp-iframe>`,
		`<link rel="preload" as="image" href="https://example.com/bar.png" crossorigin="anonymous"/><amp-iframe data-hero="" height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" src="https://example.com/bar.png" crossorigin="anonymous" i-amphtml-ssr=""></amp-img></amp-iframe>`,
	},
	{
		"data-hero: Iframe placeholder referrerpolicy",
		`<amp-iframe data-hero height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" src="https://example.com/bar.png" referrerpolicy="origin"></amp-img></amp-iframe>`,
		`<link rel="preload" as="image" href="https://example.com/bar.png" referrerpolicy="origin"/><amp-iframe data-hero="" height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" src="https://example.com/bar.png" referrerpolicy="origin" i-amphtml-ssr=""></amp-img></amp-iframe>`,
	},
	{
		"inferred-size: Iframe placeholder srcset",
		`<amp-iframe data-hero height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w"></amp-img></amp-iframe>`,
		`<link rel="preload" as="image" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w"/><amp-iframe data-hero="" height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w" i-amphtml-ssr=""></amp-img></amp-iframe>`,
	},
	{
		"data-hero: No placeholder image",
		`<html><head></head><body><amp-iframe data-hero src="/foo.html"></amp-iframe></body></html>`,
		`<html><head></head><body><amp-iframe data-hero="" src="/foo.html"></amp-iframe></body></html>`,
	},
	{
		"data-hero: iframe video placeholder",
		`<html><head></head><body><amp-video-iframe data-hero height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" src="https://example.com/foo.png"></amp-video-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video-iframe data-hero="" height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img></amp-video-iframe>`,
	},
	{
		"data-hero: iframe video placeholder crossorigin",
		`<amp-video-iframe data-hero height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" src="https://example.com/foo.png" crossorigin="anonymous"></amp-video-iframe>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png" crossorigin="anonymous"/><amp-video-iframe data-hero="" height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" src="https://example.com/foo.png" crossorigin="anonymous" i-amphtml-ssr=""></amp-img></amp-video-iframe>`,
	},
	{
		"data-hero: iframe video placeholder referrerpolicy",
		`<amp-video-iframe data-hero height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" src="https://example.com/foo.png" referrerpolicy="origin"></amp-video-iframe>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png" referrerpolicy="origin"/><amp-video-iframe data-hero="" height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" src="https://example.com/foo.png" referrerpolicy="origin" i-amphtml-ssr=""></amp-img></amp-video-iframe>`,
	},
	{
		"inferred-size: iframe video placeholder srcset",
		`<amp-video-iframe data-hero height="500" width="500" src="/foo.html"><amp-img placeholder layout="fill" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w"></amp-img></amp-video-iframe>`,
		`<link rel="preload" as="image" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w"/><amp-video-iframe data-hero="" height="500" width="500" src="/foo.html"><amp-img placeholder="" layout="fill" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w" i-amphtml-ssr=""></amp-img></amp-video-iframe>`,
	},
	{
		"data-hero: iframe video poster",
		`<html><head></head><body><amp-video-iframe data-hero height="500" width="500" src="/foo.html" poster="https://example.com/foo.png"></amp-video-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video-iframe data-hero="" height="500" width="500" src="/foo.html" poster="https://example.com/foo.png"></amp-video-iframe>`,
	},
	{
		"data-hero: iframe video poster crossorigin",
		`<html><head></head><body><amp-video-iframe data-hero height="500" width="500" src="/foo.html" poster="https://example.com/foo.png" crossorigin="anonymous"></amp-video-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video-iframe data-hero="" height="500" width="500" src="/foo.html" poster="https://example.com/foo.png" crossorigin="anonymous"></amp-video-iframe>`,
	},
	{
		"data-hero: iframe video poster referrerpolicy",
		`<html><head></head><body><amp-video-iframe data-hero height="500" width="500" src="/foo.html" poster="https://example.com/foo.png" referrerpolicy="origin"></amp-video-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video-iframe data-hero="" height="500" width="500" src="/foo.html" poster="https://example.com/foo.png" referrerpolicy="origin"></amp-video-iframe>`,
	},
	{
		"data-hero: No placeholder image",
		`<html><head></head><body><amp-video-iframe data-hero src="/foo.html"></amp-video-iframe></body></html>`,
		`<html><head></head><body><amp-video-iframe data-hero="" src="/foo.html"></amp-video-iframe></body></html>`,
	},
	{
		"data-hero: Video poster",
		`<html><head></head><body><amp-video data-hero poster="https://example.com/foo.png" width="400" height="400"><source src="foo.mp4" /></amp-video></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video data-hero="" poster="https://example.com/foo.png" width="400" height="400"><source src="foo.mp4"/></amp-video>`,
	},
	{
		"data-hero: Video poster crossorigin",
		`<html><head></head><body><amp-video data-hero poster="https://example.com/foo.png" width="400" height="400" crossorigin="anonymous"><source src="foo.mp4" /></amp-video></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video data-hero="" poster="https://example.com/foo.png" width="400" height="400" crossorigin="anonymous"><source src="foo.mp4"/></amp-video>`,
	},
	{
		"data-hero: Video poster referrerpolicy",
		`<html><head></head><body><amp-video data-hero poster="https://example.com/foo.png" width="400" height="400" referrerpolicy="origin"><source src="foo.mp4" /></amp-video></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video data-hero="" poster="https://example.com/foo.png" width="400" height="400" referrerpolicy="origin"><source src="foo.mp4"/></amp-video>`,
	},
	{
		"data-hero: amp-video with missing poster.",
		`<html><head></head><body><amp-video data-hero width="400" height="400"><source src="foo.mp4" /></amp-video></body></html>`,
		`<html><head></head><body><amp-video data-hero="" width="400" height="400"><source src="foo.mp4"/></amp-video></body></html>`,
	},
	{
		"data-hero: No display layout",
		`<html><head></head><body><amp-img data-hero height="500" src="https://example.com/foo.png" width="500" layout="nodisplay"></amp-img></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img data-hero="" height="500" src="https://example.com/foo.png" width="500" layout="nodisplay" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"data-hero: Same as above with nodisplay layout removed.",
		`<html><head></head><body><amp-img data-hero height="500" src="https://example.com/foo.png" width="500"></amp-img></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img data-hero="" height="500" src="https://example.com/foo.png" width="500" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"data-hero: Invalid protocol",
		`<html><head></head><body><amp-img data-hero width="500" height="400" src="ftp://example.com/ftp.png"></body></html>`,
		`<html><head></head><body><amp-img data-hero="" width="500" height="400" src="ftp://example.com/ftp.png"></amp-img></body></html>`,
	},
	{
		"data-hero: Srcset validity. Empty srcset.",
		`<html><head></head><body><amp-img data-hero width="500" height="400" src="https://example.com/foo.png" srcset=""></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img data-hero="" width="500" height="400" src="https://example.com/foo.png" srcset="" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"data-hero: Invalid srcset",
		`<html><head></head><body><amp-img data-hero width="500" height="400" src="https://example.com/foo.png" srcset="foo bar baz"></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img data-hero="" width="500" height="400" src="https://example.com/foo.png" srcset="foo bar baz" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"data-hero: Invalid srcset duplicates.",
		`<html><head></head><body><amp-img data-hero width="500" height="400" src="https://example.com/foo.png" srcset="foo 10w, bar 10w, baz 100w"></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img data-hero="" width="500" height="400" src="https://example.com/foo.png" srcset="foo 10w, bar 10w, baz 100w" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"data-hero: Hero image dimensions from parent container.",
		`<html><head></head><body><div width="500" height="500"><amp-img data-hero layout="fill" src="https://example.com/foo.png"></amp-img></div></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/>
<amp-img data-hero="" layout="fill" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img>
`,
	},
	{
		"data-hero: Hero image dimesions from parent container, too small.",
		`<html><head></head><body><div width="50" height="50"><amp-img data-hero layout="fill" src="https://example.com/foo.png"></amp-img></div></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/>
<amp-img data-hero="" layout="fill" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img>
`,
	},
	{
		"data-hero: No dimensions in parent containers.",
		`<html><head></head><body><div><amp-img data-hero layout="fill" src="https://example.com/foo.png"></amp-img></div></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/>
<amp-img data-hero="" layout="fill" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img>
`,
	},
	{
		"data-hero: No dimension from parent because layout is not responsive or fill",
		`<html><head></head><body><div><amp-img data-hero src="https://example.com/foo.png"></amp-img></div></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/>
<amp-img data-hero="" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img>
`,
	},
}

var testcaseDataHeroImg = []TestCase{
	{
		"data-hero",
		`<html><head></head><body><img data-hero width="500" height="400" src="https://example.com/foo.png"/></body></html>`,
		`<html><head><link rel="preload" as="image" href="https://example.com/foo.png"/></head><body><img data-hero="" width="500" height="400" src="https://example.com/foo.png"/></body></html>`,
	},
	{
		"data-hero: Allows multiple heros",
		``,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><link rel="preload" as="image" href="https://example.com/bar.png"/>`,
	},
	{
		"data-hero: Prioritizes data-hero",
		`<html><head></head><body><img width="100" height="100" src="https://example.com/foo.png"/><img data-hero width="500" height="400" src="https://example.com/bar.png"/></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/bar.png"/>`,
	},
	{
		"data-hero: Prevents size-inferred hero",
		`<html><head></head><body><img data-hero width="500" height="400" src="https://example.com/foo.png"/><img width="500" height="400" src="https://example.com/bar.png"/></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/>`,
	},
	{
		"data-hero: Dimensions too small",
		`<html><head></head><body><img data-hero height="100" src="https://example.com/foo.png" width="100"/></body></html>`,
		`<html><head><link rel="preload" as="image" href="https://example.com/foo.png"/></head><body><img data-hero="" height="100" src="https://example.com/foo.png" width="100"/></body></html>`,
	},
	{
		"data-hero: Crossorigin attribute.",
		`<html><head></head><body><img data-hero width="500" height="400" src="https://example.com/foo.png" crossorigin="anonymous"/></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png" crossorigin="anonymous"/>`,
	},
	{
		"data-hero: Referrerpolicy referrerpolicy.",
		`<html><head></head><body><img data-hero width="500" height="400" src="https://example.com/foo.png" referrerpolicy="origin"/></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png" referrerpolicy="origin"/>`,
	},
	{
		"data-hero: Srcset attribute.",
		``,
		`<link rel="preload" as="image" href="https://example.com/foo.png" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w"/>`,
	},
	{
		"inferred-size: Srcset without src.",
		`<html><head></head><body><img data-hero width="500" height="400" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w"/></body></html>`,
		`<link rel="preload" as="image" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w"/>`,
	},
	{
		"inferred-size: Sizes.",
		`<html><head></head><body><img data-hero width="500" height="400" srcset="https://example.com/foomedium.png 800w, https://example.com/foolarge.png 1200w" sizes="100vw"/></body></html>`,
		`<link rel="preload" as="image" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w" imagesizes="100vw"/>`,
	},
	{
		"data-hero: Iframe placeholder",
		`<html><head></head><body><amp-iframe data-hero height="500" width="500" src="/foo.html"><img placeholder src="https://example.com/bar.png"/></amp-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/bar.png"/><amp-iframe data-hero="" height="500" width="500" src="/foo.html"></amp-iframe>`,
	},
	{
		"data-hero: Iframe placeholder crossorigin",
		`<html><head></head><body><amp-iframe data-hero height="500" width="500" src="/foo.html"><img placeholder src="https://example.com/bar.png" crossorigin="anonymous"/></amp-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/bar.png" crossorigin="anonymous"/><amp-iframe data-hero="" height="500" width="500" src="/foo.html"></amp-iframe>`,
	},
	{
		"data-hero: Iframe placeholder referrerpolicy",
		`<html><head></head><body><amp-iframe data-hero height="500" width="500" src="/foo.html"><img placeholder src="https://example.com/bar.png" referrerpolicy="origin"/></amp-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/bar.png" referrerpolicy="origin"/><amp-iframe data-hero="" height="500" width="500" src="/foo.html"></amp-iframe>`,
	},
	{
		"inferred-size: Iframe placeholder srcset",
		`<amp-iframe data-hero height="500" width="500" src="/foo.html"></amp-iframe>`,
		`<link rel="preload" as="image" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w"/><amp-iframe data-hero="" height="500" width="500" src="/foo.html"></amp-iframe>`,
	},
	{
		"data-hero: No placeholder image",
		`<html><head></head><body><amp-iframe data-hero src="/foo.html"></amp-iframe></body></html>`,
		`<html><head></head><body><amp-iframe data-hero="" src="/foo.html"></amp-iframe></body></html>`,
	},
	{
		"data-hero: iframe video placeholder",
		`<html><head></head><body><amp-video-iframe data-hero height="500" width="500" src="/foo.html"><img placeholder src="https://example.com/foo.png"/></amp-video-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video-iframe data-hero="" height="500" width="500" src="/foo.html"></amp-video-iframe>`,
	},
	{
		"data-hero: iframe video placeholder crossorigin",
		`<amp-video-iframe data-hero height="500" width="500" src="/foo.html"></amp-video-iframe>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png" crossorigin="anonymous"/><amp-video-iframe data-hero="" height="500" width="500" src="/foo.html"></amp-video-iframe>`,
	},
	{
		"data-hero: iframe video placeholder referrerpolicy",
		`<amp-video-iframe data-hero height="500" width="500" src="/foo.html"></amp-video-iframe>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png" referrerpolicy="origin"/><amp-video-iframe data-hero="" height="500" width="500" src="/foo.html"></amp-video-iframe>`,
	},
	{
		"inferred-size: iframe video placeholder srcset",
		`<amp-video-iframe data-hero height="500" width="500" src="/foo.html"></amp-video-iframe>`,
		`<link rel="preload" as="image" imagesrcset="https://example.com/foolarge.png 1200w, https://example.com/foomedium.png 800w"/><amp-video-iframe data-hero="" height="500" width="500" src="/foo.html"></amp-video-iframe>`,
	},
	{
		"data-hero: iframe video poster",
		`<html><head></head><body><amp-video-iframe data-hero height="500" width="500" src="/foo.html" poster="https://example.com/foo.png"></amp-video-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video-iframe data-hero="" height="500" width="500" src="/foo.html" poster="https://example.com/foo.png"></amp-video-iframe>`,
	},
	{
		"data-hero: iframe video poster crossorigin",
		`<html><head></head><body><amp-video-iframe data-hero height="500" width="500" src="/foo.html" poster="https://example.com/foo.png" crossorigin="anonymous"></amp-video-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video-iframe data-hero="" height="500" width="500" src="/foo.html" poster="https://example.com/foo.png" crossorigin="anonymous"></amp-video-iframe>`,
	},
	{
		"data-hero: iframe video poster referrerpolicy",
		`<html><head></head><body><amp-video-iframe data-hero height="500" width="500" src="/foo.html" poster="https://example.com/foo.png" referrerpolicy="origin"></amp-video-iframe></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video-iframe data-hero="" height="500" width="500" src="/foo.html" poster="https://example.com/foo.png" referrerpolicy="origin"></amp-video-iframe>`,
	},
	{
		"data-hero: No placeholder image",
		`<html><head></head><body><amp-video-iframe data-hero src="/foo.html"></amp-video-iframe></body></html>`,
		`<html><head></head><body><amp-video-iframe data-hero="" src="/foo.html"></amp-video-iframe></body></html>`,
	},
	{
		"data-hero: Video poster",
		`<html><head></head><body><amp-video data-hero poster="https://example.com/foo.png" width="400" height="400"><source src="foo.mp4" /></amp-video></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video data-hero="" poster="https://example.com/foo.png" width="400" height="400"><source src="foo.mp4"/></amp-video>`,
	},
	{
		"data-hero: Video poster crossorigin",
		`<html><head></head><body><amp-video data-hero poster="https://example.com/foo.png" width="400" height="400" crossorigin="anonymous"><source src="foo.mp4" /></amp-video></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video data-hero="" poster="https://example.com/foo.png" width="400" height="400" crossorigin="anonymous"><source src="foo.mp4"/></amp-video>`,
	},
	{
		"data-hero: Video poster referrerpolicy",
		`<html><head></head><body><amp-video data-hero poster="https://example.com/foo.png" width="400" height="400" referrerpolicy="origin"><source src="foo.mp4" /></amp-video></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-video data-hero="" poster="https://example.com/foo.png" width="400" height="400" referrerpolicy="origin"><source src="foo.mp4"/></amp-video>`,
	},
	{
		"data-hero: amp-video with missing poster.",
		`<html><head></head><body><amp-video data-hero width="400" height="400"><source src="foo.mp4" /></amp-video></body></html>`,
		`<html><head></head><body><amp-video data-hero="" width="400" height="400"><source src="foo.mp4"/></amp-video></body></html>`,
	},
	{
		"data-hero: No display layout",
		`<html><head></head><body><img data-hero height="500" src="https://example.com/foo.png" width="500" layout="nodisplay"/></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/>`,
	},
	{
		"data-hero: Same as above with nodisplay layout removed.",
		`<html><head></head><body><img data-hero height="500" src="https://example.com/foo.png" width="500"/></body></html>`,
		`<html><head><link rel="preload" as="image" href="https://example.com/foo.png"/></head><body><img data-hero="" height="500" src="https://example.com/foo.png" width="500"/></body></html>`,
	},
	{
		"data-hero: Invalid protocol",
		`<html><head></head><body><img data-hero width="500" height="400" src="ftp://example.com/ftp.png"/></body></html>`,
		`<html><head></head><body><img data-hero="" width="500" height="400" src="ftp://example.com/ftp.png"/></body></html>`,
	},
	{
		"data-hero: Srcset validity. Empty srcset.",
		`<html><head></head><body><img data-hero width="500" height="400" src="https://example.com/foo.png" srcset=""/></body></html>`,
		`<html><head><link rel="preload" as="image" href="https://example.com/foo.png"/></head><body><img data-hero="" width="500" height="400" src="https://example.com/foo.png" srcset=""/></body></html>`,
	},
	{
		"data-hero: Invalid srcset",
		`<html><head></head><body><img data-hero width="500" height="400" src="https://example.com/foo.png" srcset="foo bar baz"/></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/>`,
	},
	{
		"data-hero: Invalid srcset duplicates.",
		`<html><head></head><body><img data-hero width="500" height="400" src="https://example.com/foo.png" srcset="foo 10w, bar 10w, baz 100w"/></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/>`,
	},
	{
		"data-hero: Hero image dimensions from parent container.",
		`<html><head></head><body><div width="500" height="500"><img data-hero src="https://example.com/foo.png"/></div></body></html>`,
		`<html><head><link rel="preload" as="image" href="https://example.com/foo.png"/></head><body><div width="500" height="500"><img data-hero="" src="https://example.com/foo.png"/></div></body></html>`,
	},
	{
		"data-hero: Hero image dimesions from parent container, too small.",
		`<html><head></head><body><div width="50" height="50"><img data-hero src="https://example.com/foo.png"/></div></body></html>`,
		`<html><head><link rel="preload" as="image" href="https://example.com/foo.png"/></head><body><div width="50" height="50"><img data-hero="" src="https://example.com/foo.png"/></div></body></html>`,
	},
	{
		"data-hero: No dimensions in parent containers.",
		`<html><head></head><body><div><img data-hero src="https://example.com/foo.png"/></div></body></html>`,
		`<html><head><link rel="preload" as="image" href="https://example.com/foo.png"/></head><body><div><img data-hero="" src="https://example.com/foo.png"/></div></body></html>`,
	},
	{
		"data-hero: No dimension from parent because layout is not responsive or fill",
		`<html><head></head><body><div><img data-hero src="https://example.com/foo.png"/></div></body></html>`,
		`<html><head><link rel="preload" as="image" href="https://example.com/foo.png"/></head><body><div><img data-hero="" src="https://example.com/foo.png"/></div></body></html>`,
	},
}

var testLazyLoadImg = []TestCase{
	{
		"data-hero leftover",
		`<amp-img data-hero width="500" height="400" src="https://example.com/foo.png"></amp-img><amp-img width="500" height="400" src="https://example.com/bar.png"></amp-img>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img data-hero="" width="500" height="400" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img><amp-img width="500" height="400" src="https://example.com/bar.png" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"inferred-size leftover",
		`<html><head></head><body><amp-img width="500" height="400" src="https://example.com/foo.png"></amp-img><amp-img width="100" height="100" src="https://example.com/bar.png"></amp-img></body></html>`,
		`<link rel="preload" as="image" href="https://example.com/foo.png"/><amp-img width="500" height="400" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img><amp-img width="100" height="100" src="https://example.com/bar.png" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"no transformed images",
		`<html><head></head><body><amp-img width="100" height="100" src="https://example.com/foo.png"></amp-img></body></html>`,
		`<amp-img width="100" height="100" src="https://example.com/foo.png" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"crossorigin propagates",
		`<html><head></head><body><amp-img width="100" height="100" src="https://example.com/foo.png" crossorigin="anonymous"></amp-img></body></html>`,
		`<amp-img width="100" height="100" src="https://example.com/foo.png" crossorigin="anonymous" i-amphtml-ssr=""></amp-img>`,
	},
	{
		"referrerpolicy propagates",
		`<html><head></head><body><amp-img width="100" height="100" src="https://example.com/foo.png" referrerpolicy="origin"></amp-img></body></html>`,
		`<amp-img width="100" height="100" src="https://example.com/foo.png" referrerpolicy="origin" i-amphtml-ssr=""></amp-img>`,
	},
}

func TestInferSizeCases(t *testing.T) {
	testCases(t, testcaseInferSize, 0)
}

func TestDataHeroCasesAmpImg(t *testing.T) {
	testCases(t, testcaseDataHeroAmpImg, 0)
}

func TestDataHeroCasesImg(t *testing.T) {
	testCases(t, testcaseDataHeroImg, 0)
}

func TestLazyLoadCases(t *testing.T) {
	testCases(t, testLazyLoadImg, 5)
}

repository: ampproject/amppackager

repo_name: ampproject/amppackager

branch_name: refs/heads/releases

gha_language: Go


text:
# base recipe: meta/recipes-support/libevent/libevent_2.1.8.bb
# base branch: warrior
# base commit: 4e479cee5d87155e2a90801496033f1a03c5ab30

SUMMARY = "An asynchronous event notification library"
HOMEPAGE = "http://libevent.org/"
BUGTRACKER = "https://github.com/libevent/libevent/issues"
SECTION = "libs"

LICENSE = "BSD & MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=17f20574c0b154d12236d5fbe964f549"

inherit debian-package
require recipes-debian/sources/${BPN}.inc

FILESPATH_append = ":${COREBASE}/meta/recipes-support/${BPN}/${BPN}"
SRC_URI += "\
    file://Makefile-missing-test-dir.patch \
    file://0001-test-fix-32bit-linux-regress.patch \
    file://run-ptest \
"

UPSTREAM_CHECK_URI = "http://libevent.org/"

PACKAGECONFIG ??= ""
PACKAGECONFIG[openssl] = "--enable-openssl,--disable-openssl,openssl"

inherit autotools

# Needed for Debian packaging
LEAD_SONAME = "libevent-2.1.so"

inherit ptest multilib_header

DEPENDS = "zlib"

BBCLASSEXTEND = "native nativesdk"

CVE_VERSION = "${@d.getVar('PV').split('-')[0]}"

do_install_append() {
        oe_multilib_header event2/event-config.h
}

do_install_ptest() {
	install -d ${D}${PTEST_PATH}/test
	for file in ${B}/test/.libs/regress ${B}/test/.libs/test*
	do
		install -m 0755 $file ${D}${PTEST_PATH}/test
	done
}

license: MIT

repository: miraclelinux/meta-debian-extended

license_type: permissive

repo_name: miraclelinux/meta-debian-extended

branch_name: refs/heads/warrior

gha_license_id: MIT

gha_language: BitBake

extension: bb


text:
SUMMARY = "Zstandard - Fast real-time compression algorithm"
HOMEPAGE = "http://www.zstd.net"
LICENSE = "BSD & GPLv2"
LIC_FILES_CHKSUM = "file://LICENSE;md5=c7f0b161edbe52f5f345a3d1311d0b32 \
                    file://contrib/linux-kernel/COPYING;md5=39bba7d2cf0ba1036f2a6e2be52fe3f0"
SRCREV = "f3a8bd553a865c59f1bd6e1f68bf182cf75a8f00"
PV = "1.3.3+git${SRCPV}"
# required as we started with a wrong version (1.33)
PE = "1"

SRC_URI = "git://github.com/facebook/zstd.git;protocol=https;nobranch=1"

S = "${WORKDIR}/git"

EXTRA_OECMAKE_append = " -DTHREADS_PTHREAD_ARG=0"

do_install() {
    oe_runmake DESTDIR=${D} install
}

BBCLASSEXTEND = "native nativesdk"

repository: san-skov-dk/meta-rauc

repo_name: san-skov-dk/meta-rauc

branch_name: refs/heads/master


text:
# Copyright (C) 2018 Nathan Genetzky <nathan@genetzky.us>
# Released under the MIT license (see COPYING.MIT for the terms)

require bitbake-oe.inc

PV = "2.4"
PR = "${INC_PR}.2"

repository: NGenetzky/meta-ngenetzky

repo_name: NGenetzky/meta-ngenetzky


text:
package storage

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/google/go-github/v26/github"
	"golang.org/x/oauth2"
)

type client struct {
	*github.Client
}

func NewClient() *client {

	oauth2Token := oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")}
	githubClient := github.NewClient(oauth2.NewClient(
		context.Background(), oauth2.StaticTokenSource(&oauth2Token)))
	return &client{Client: githubClient}
}

func (c *client) SaveFile(filename, content string) error {

	ctx := context.Background()
	baseRef, _, err := c.Git.GetRef(ctx, "githubName", "notes", "refs/heads/master")
	if err != nil {
		return err
	}

	newRef := &github.Reference{
		Ref:    github.String("refs/heads/master"),
		Object: &github.GitObject{SHA: baseRef.Object.SHA}}

	t := []github.TreeEntry{github.TreeEntry{
		Path: github.String(filename), Type: github.String("blob"),
		Content: github.String(content), Mode: github.String("100644")}}

	tree, _, err := c.Git.CreateTree(ctx, "githubName", "notes", *newRef.Object.SHA, t)
	if err != nil {
		return err
	}

	parent, _, err := c.Repositories.GetCommit(ctx, "githubName", "notes", *newRef.Object.SHA)
	if err != nil {
		return err
	}

	parent.Commit.SHA = parent.SHA
	commitMessage := fmt.Sprintf("test commit")
	curTime := time.Now()
	author := &github.CommitAuthor{
		Date:  &curTime,
		Name:  github.String("githubName"),
		Email: github.String("githubName@gmail.com"),
	}

	commitInfo := &github.Commit{Author: author, Message: github.String(commitMessage),
		Tree: tree, Parents: []github.Commit{*parent.Commit}}

	commit, _, err := c.Git.CreateCommit(ctx, "githubName", "notes", commitInfo)
	if err != nil {
		return err
	}

	newRef.Object.SHA = commit.SHA
	reference, _, err := c.Git.UpdateRef(ctx, "githubName", "notes", newRef, false)
	if err != nil {
		return err
	}
	fmt.Printf("Successfully committed %+v\n", commit)
	fmt.Printf("Successfully committed reference is %+v\n", reference)
	return nil
}

repository: prateekgogia/bear-notes-sync

repo_name: prateekgogia/bear-notes-sync

extension: go


text:
DESCRIPTION = "Android Image Creation and Booting Utilities"
SECTION = "console/utils"
LICENSE = "GPL"
SRCREV = "2812c0ff528e85b394c52cef736ecc07393b8d27"
PV = "1.0+gitr${SRCREV}"
PR = "r1"

SRC_URI = "\
  ${FREESMARTPHONE_GIT}/utilities.git;protocol=git;branch=master \
"
S = "${WORKDIR}/git/android/image-utils"

inherit autotools native

do_deploy () {
	install -d ${DEPLOY_DIR_TOOLS}
	install -m 0755 fastboot/fastboot ${DEPLOY_DIR_TOOLS}
}
do_deploy[dirs] = "${S}"
addtask deploy before do_build after do_compile

repository: overo/overo-oe-natty

repo_name: overo/overo-oe-natty

branch_name: HEAD


text:
#pragma once
#include "CoreMinimal.h"
#include "Components/SlateWrapperTypes.h"
#include "Blueprint/UserWidget.h"
#include "VisibilityChangedDelegateDelegate.h"
#include "FSDUserWidget.generated.h"

class AFSDPlayerState;
class AItem;
class APlayerCharacter;

UCLASS(Blueprintable, EditInlineNew)
class FSD_API UFSDUserWidget : public UUserWidget {
    GENERATED_BODY()
public:
    UPROPERTY(BlueprintAssignable, BlueprintReadWrite, EditAnywhere, meta=(AllowPrivateAccess=true))
    FVisibilityChangedDelegate OnVisibilityChangedFSD;

protected:
    UPROPERTY(BlueprintReadWrite, EditAnywhere, Transient, meta=(AllowPrivateAccess=true))
    APlayerCharacter* Character;

public:
    UFSDUserWidget();
protected:
    UFUNCTION(BlueprintCallable)
    void SetOpacity(float alpha);

public:
    UFUNCTION(BlueprintCallable)
    void ScaleByBaseResolution();

protected:
    UFUNCTION(BlueprintCallable)
    void Refresh();

    UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
    void ReceiveNewVisibility(ESlateVisibility NewVisibility);

    UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
    void OnCloseMenuReleased(bool& Handled);

    UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
    void OnCloseMenuPressed(bool& Handled);

    UFUNCTION(BlueprintCallable)
    bool HasCharacter();

    UFUNCTION(BlueprintCallable, BlueprintPure)
    float GetOpacity() const;

    UFUNCTION(BlueprintCallable, BlueprintPure)
    AFSDPlayerState* GetFSDPlayerState();

    UFUNCTION(BlueprintCallable, BlueprintPure)
    AItem* GetEquippedItem() const;

public:
    UFUNCTION(BlueprintCallable, BlueprintPure)
    float GetBaseResolutionScale() const;

protected:
    UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
    void BP_OnRefresh();

};

repository: SamsDRGMods/DRGLib

repo_name: SamsDRGMods/DRGLib

branch_name: refs/heads/main

extension: h


text:
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_KEYED_SERVICE_CORE_KEYED_SERVICE_FACTORY_H_
#define COMPONENTS_KEYED_SERVICE_CORE_KEYED_SERVICE_FACTORY_H_

#include <map>
#include <memory>

#include "base/callback_forward.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "components/keyed_service/core/keyed_service_base_factory.h"
#include "components/keyed_service/core/keyed_service_export.h"

class DependencyManager;
class KeyedService;

// Base class for Factories that take a base::SupportsUserData object and return
// some service on a one-to-one mapping. Each concrete factory that derives from
// this class *must* be a Singleton (only unit tests don't do that).
//
// We do this because services depend on each other and we need to control
// shutdown/destruction order. In each derived classes' constructors, the
// implementors must explicitly state which services are depended on.
class KEYED_SERVICE_EXPORT KeyedServiceFactory
    : public KeyedServiceBaseFactory {
 protected:
  KeyedServiceFactory(const char* name, DependencyManager* manager);
  ~KeyedServiceFactory() override;

  // A function that supplies the instance of a KeyedService for a given
  // |context|. This is used primarily for testing, where we want to feed
  // a specific mock into the KeyedServiceFactory system.
  typedef std::unique_ptr<KeyedService> (*TestingFactoryFunction)(
      base::SupportsUserData* context);

  // Associates |factory| with |context| so that |factory| is used to create
  // the KeyedService when requested.  |factory| can be NULL to signal that
  // KeyedService should be NULL. Multiple calls to SetTestingFactory() are
  // allowed; previous services will be shut down.
  void SetTestingFactory(base::SupportsUserData* context,
                         TestingFactoryFunction factory);

  // Associates |factory| with |context| and immediately returns the created
  // KeyedService. Since the factory will be used immediately, it may not be
  // NULL.
  KeyedService* SetTestingFactoryAndUse(base::SupportsUserData* context,
                                        TestingFactoryFunction factory);

  // Common implementation that maps |context| to some service object. Deals
  // with incognito contexts per subclass instructions with GetContextToUse()
  // method on the base.  If |create| is true, the service will be created
  // using BuildServiceInstanceFor() if it doesn't already exist.
  KeyedService* GetServiceForContext(base::SupportsUserData* context,
                                     bool create);

  // Maps |context| to |service| with debug checks to prevent duplication.
  void Associate(base::SupportsUserData* context,
                 std::unique_ptr<KeyedService> service);

  // Removes the mapping from |context| to a service.
  void Disassociate(base::SupportsUserData* context);

  // Returns a new KeyedService that will be associated with |context|.
  virtual std::unique_ptr<KeyedService> BuildServiceInstanceFor(
      base::SupportsUserData* context) const = 0;

  // Returns whether the |context| is off-the-record or not.
  virtual bool IsOffTheRecord(base::SupportsUserData* context) const = 0;

  // KeyedServiceBaseFactory:
  void ContextShutdown(base::SupportsUserData* context) override;
  void ContextDestroyed(base::SupportsUserData* context) override;

  void SetEmptyTestingFactory(base::SupportsUserData* context) override;
  bool HasTestingFactory(base::SupportsUserData* context) override;
  void CreateServiceNow(base::SupportsUserData* context) override;

 private:
  friend class DependencyManager;
  friend class DependencyManagerUnittests;

  typedef std::map<base::SupportsUserData*, KeyedService*> KeyedServices;
  typedef std::map<base::SupportsUserData*, TestingFactoryFunction>
      OverriddenTestingFunctions;

  // The mapping between a context and its service.
  KeyedServices mapping_;

  // The mapping between a context and its overridden
  // TestingFactoryFunction.
  OverriddenTestingFunctions testing_factories_;

  DISALLOW_COPY_AND_ASSIGN(KeyedServiceFactory);
};

#endif  // COMPONENTS_KEYED_SERVICE_CORE_KEYED_SERVICE_FACTORY_H_

repository: metux/chromium-suckless

repo_name: metux/chromium-suckless

branch_name: refs/heads/orig


text:
DESCRIPTION = "OpenDreambox: Enigma2 Task for the OpenDreambox Distribution"
SECTION = "opendreambox/base"
LICENSE = "MIT"
PR = "r8"

inherit task

PROVIDES = "\
  task-opendreambox-ui \
  ${PACKAGES} \
"

PACKAGES = "\
  task-opendreambox-enigma2 \
"

DEPENDS += "\
  ${@base_contains("MACHINE_FEATURES", "dreambox-libpassthrough", "dreambox-libpassthrough", "", d)} \
"

#
# task-opendreambox-enigma2
#
RPROVIDES_task-opendreambox-enigma2 = "task-opendreambox-ui"
DESCRIPTION_task-opendreambox-enigma2 = "OpenDreambox: Enigma2 Dependencies"
RDEPENDS_task-opendreambox-enigma2 = "\
  dreambox-blindscan-utils \
  enigma2 \
  enigma2-defaultservices \
  enigma2-plugin-extensions-mediascanner \
  enigma2-plugin-systemplugins-frontprocessorupgrade \
  enigma2-plugin-systemplugins-hotplug \
  enigma2-plugin-systemplugins-networkwizard \
  enigma2-plugin-systemplugins-softwaremanager \
  enigma2-plugin-systemplugins-videotune \
  enigma2-streamproxy \
  tuxbox-tuxtxt-32bpp \
  enigma2-meta \
  enigma2-plugins-meta \
  enigma2-skins-meta \
"

RRECOMMENDS_task-opendreambox-enigma2 = "\
  aio-grab \
  python-crypt \
  python-netserver \
  python-twisted-core \
  python-twisted-protocols \
  python-twisted-web \
  enigma2-plugin-extensions-cutlisteditor \
  enigma2-plugin-extensions-graphmultiepg \
  enigma2-plugin-extensions-mediaplayer \
  enigma2-plugin-extensions-pictureplayer \
  enigma2-plugin-extensions-webinterface \
  enigma2-plugin-systemplugins-satfinder \
  enigma2-plugin-systemplugins-positionersetup \
  enigma2-plugin-systemplugins-skinselector \
  ${@base_contains("MACHINE_FEATURES", "wifi", "task-opendreambox-wlan", "", d)} \
  ${@base_contains("MACHINE_FEATURES", "modem", "task-opendreambox-modem", "", d)} \
  ${@base_contains("MACHINE_FEATURES", "dreambox-libpassthrough", "libpassthrough", "", d)} \
"

RDEPENDS_task-opendreambox-enigma2_append_dm500hd = "\
  enigma2-plugin-extensions-genuinedreambox \
  enigma2-plugin-systemplugins-videomode \
  enigma2-plugin-systemplugins-tempfancontrol \
"

RDEPENDS_task-opendreambox-enigma2_append_dm800 = "\
  enigma2-plugin-extensions-genuinedreambox \
  enigma2-plugin-systemplugins-videomode \
"

RDEPENDS_task-opendreambox-enigma2_append_dm800se = "\
  enigma2-plugin-extensions-genuinedreambox \
  enigma2-plugin-systemplugins-videomode \
  enigma2-plugin-systemplugins-tempfancontrol \
"

RDEPENDS_task-opendreambox-enigma2_append_dm8000 = "\
  enigma2-plugin-extensions-genuinedreambox \
  enigma2-plugin-systemplugins-commoninterfaceassignment \
  enigma2-plugin-systemplugins-videomode \
  enigma2-plugin-systemplugins-nfiflash \
"

RDEPENDS_task-opendreambox-enigma2_append_dm7020hd = "\
  enigma2-plugin-extensions-genuinedreambox \
  enigma2-plugin-systemplugins-commoninterfaceassignment \
  enigma2-plugin-systemplugins-videomode \
  enigma2-plugin-systemplugins-nfiflash \
"

RRECOMMENDS_task-opendreambox-enigma2_append_dm8000 = "\
  task-opendreambox-cdplayer \
  task-opendreambox-dvdplayer \
  task-opendreambox-dvdburn \
"

PACKAGE_ARCH = "${MACHINE_ARCH}"

repository: demsey/openembedded

repo_name: demsey/openembedded


text:
namespace Facebook.Web.Tests.FacebookSession.ParseUserIdFromAccessToken
{
    using Facebook;
    using Xunit;
    using Xunit.Extensions;

    public class GivenInvalidUserAccessTokenWithMoreThan2Pipelinesthen
    {
        [Theory]
        [InlineData("two|pipe|lines")]
        [InlineData("three|pipe|line|s")]
        [InlineData("four|pipe|li|ne|s")]
        public void ResultShouldBeNull(string accessToken)
        {
            var userId = FacebookSession.ParseUserIdFromAccessToken(accessToken);

            Assert.Null(userId);
        }
    }
}

license: MS-PL

repository: dovanduy/thanhpham

repo_name: dovanduy/thanhpham

extension: cs


text:
package plusone

func plusOne(digits []int) []int {
	var s []int
	var carry, n int
	length := len(digits) - 1

	for i := length; i >= 0; i-- {
		if i == length {
			n = digits[i] + 1
		} else {
			n = digits[i] + carry
		}
		if n >= 10 {
			carry = n / 10
			n = n % 10
		} else {
			carry = 0
		}
		s = append(s, n)
	}

	if carry > 0 {
		s = append(s, carry)
	}

	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
		s[i], s[j] = s[j], s[i]
	}
	return s
}

repository: alandtsang/leetcode-go

repo_name: alandtsang/leetcode-go


text:
package backups

import (
	"github.com/huaweicloud/golangsdk"
)

type BackupOpts struct {

	// Specifies the snapshot object.
	Backup struct {

		// Specifies the backup description. It contains a maximum of 256 characters and cannot contain the following special characters: !<>=&"'
		Description string `json:"description,omitempty"`

		// Specifies a primary DB instance ID.
		Instance string `json:"instance,omitempty"`

		// Specifies the backup name. It must be 4 to 64 characters in length and start with a letter. It is case-insensitive and can contain only letters, digits, hyphens (-), and underscores (_).
		Name string `json:"name,omitempty"`
	} `json:"backup,"`
}

type BackupOptsBuilder interface {
	ToBackupMap() (map[string]interface{}, error)
}

func (opts BackupOpts) ToBackupMap() (map[string]interface{}, error) {
	b, err := golangsdk.BuildRequestBody(opts, "")
	if err != nil {
		return nil, err
	}
	return b, nil
}

func Backup(client *golangsdk.ServiceClient, versionId string, projectId string, opts BackupOptsBuilder) (r BackupResult) {
	b, _ := opts.ToBackupMap()

	_, r.Err = client.Post(BackupURL(client, versionId, projectId), b, &r.Body, &golangsdk.RequestOpts{
		OkCodes:     []int{200, 201, 202, 204},
		MoreHeaders: map[string]string{"X-Language": "en-us", "Content-Type": "application/json"},
	})
	return
}

type CreatePolicyOpts struct {

	// Specifies the backup policy object, including the backup retention period (days) and start time.
	Policy struct {

		// Specifies the number of days to retain the generated backup files. Its value range is from 0 to 35. If this parameter is not specified or set to 0, the automated backup policy is disabled.
		Keepday int `json:"keepday,"`

		// Indicates the backup start time that has been set. Valid value: The value cannot be empty. The format can be hh:mm:ss or hh:mm and must be valid. The current time is the UTC time.
		Starttime string `json:"starttime,"`
	} `json:"policy,"`
}

type CreatePolicyOptsBuilder interface {
	ToCreatePolicyMap() (map[string]interface{}, error)
}

func (opts CreatePolicyOpts) ToCreatePolicyMap() (map[string]interface{}, error) {
	b, err := golangsdk.BuildRequestBody(opts, "")
	if err != nil {
		return nil, err
	}
	return b, nil
}

func CreatePolicy(client *golangsdk.ServiceClient, versionId string, projectId string, instanceId string, opts CreatePolicyOptsBuilder) (r CreatePolicyResult) {
	b, _ := opts.ToCreatePolicyMap()

	_, r.Err = client.Put(CreatePolicyURL(client, versionId, projectId, instanceId), b, &r.Body, &golangsdk.RequestOpts{
		OkCodes:     []int{200, 201, 202, 204},
		MoreHeaders: map[string]string{"X-Language": "en-us", "Content-Type": "application/json"},
	})
	return
}

func Delete(client *golangsdk.ServiceClient, versionId string, projectId string, backupId string) (r DeleteResult) {
	url := DeleteURL(client, versionId, projectId, backupId)
	_, r.Err = client.Delete(url, &golangsdk.RequestOpts{
		JSONResponse: &r.Body,
		OkCodes:      []int{200, 201, 202, 204},
		MoreHeaders:  map[string]string{"X-Language": "en-us", "Content-Type": "application/json"},
	})
	return
}

func GetPolicy(client *golangsdk.ServiceClient, versionId string, projectId string, instanceId string) (r GetPolicyResult) {
	url := GetPolicyURL(client, versionId, projectId, instanceId)
	_, r.Err = client.Get(url, &r.Body, &golangsdk.RequestOpts{
		OkCodes:     []int{200, 201, 202, 204},
		MoreHeaders: map[string]string{"X-Language": "en-us", "Content-Type": "application/json"},
	})
	return
}

func List(client *golangsdk.ServiceClient, versionId string, projectId string) (r ListResult) {
	url := ListURL(client, versionId, projectId)
	_, r.Err = client.Get(url, &r.Body, &golangsdk.RequestOpts{
		OkCodes:     []int{200, 201, 202, 204},
		MoreHeaders: map[string]string{"X-Language": "en-us", "Content-Type": "application/json"},
	})
	return
}

type RestoreOpts struct {

	// Specifies the restore information, including backupRef and restoreTime. At least one of them must be specified. If both of them are specified, only backupRef takes effect.
	Restore struct {

		// Specifies the full backup file ID.
		BackupRef string `json:"backupRef,omitempty"`

		// Specifies the time point to which the DB instance is restored.
		RestoreTime int64 `json:"restoreTime,omitempty"`
	} `json:"restore,"`
}

type RestoreOptsBuilder interface {
	ToRestoreMap() (map[string]interface{}, error)
}

func (opts RestoreOpts) ToRestoreMap() (map[string]interface{}, error) {
	b, err := golangsdk.BuildRequestBody(opts, "")
	if err != nil {
		return nil, err
	}
	return b, nil
}

func Restore(client *golangsdk.ServiceClient, versionId string, projectId string, instanceId string, opts RestoreOptsBuilder) (r RestoreResult) {
	b, _ := opts.ToRestoreMap()

	_, r.Err = client.Post(RestoreURL(client, versionId, projectId, instanceId), b, &r.Body, &golangsdk.RequestOpts{
		OkCodes:     []int{200, 201, 202, 204},
		MoreHeaders: map[string]string{"X-Language": "en-us", "Content-Type": "application/json"},
	})
	return
}

type RestoreWithNewOpts struct {

	// Specifies new DB instance details.
	Instance struct {

		// Specifies the DB instance name. The DB instance name of the same DB engine is unique for the same tenant. Valid value: The value must be 4 to 64 characters in length and start with a letter. It is case-insensitive and can contain only letters, digits, hyphens (-), and underscores (_).
		Name string `json:"name,"`

		// Specifies the specification ID (flavors.id in the response message in section Obtaining All DB Instance Specifications). Valid value: The value cannot be empty. The name length and whether the name complies with UUID regular expression rules are verified.
		FlavorRef string `json:"flavorRef,"`

		// Specifies the volume information. This parameter is mandatory for single or HA DB instance creation.
		Volume struct {

			// Specifies the volume size in GB. It cannot be less than the volume size of the original DB instance.
			Size int `json:"size,"`
		} `json:"volume,"`

		// Specifies the HA configuration parameter. It is mandatory for HA DB instance creation.
		Ha struct {

			// Specifies the HA configuration parameter. Valid value: The value is true or false. The value true indicates creating HA DB instances. The value false indicates creating a single DB instance.
			Enable bool `json:"enable,omitempty"`

			// Specifies the replication mode for the standby DB instance. The value cannot be empty. For MySQL, the value is async or semisync. For HWSQL, the value is async or semisync. For PostgreSQL, the value is async or sync. For Microsoft SQL Server, the value is sync.
			ReplicationMode bool `json:"replicationMode,omitempty"`
		} `json:"ha,omitempty"`

		// Restores data to a new DB instance.
		RestorePoint struct {

			// Specifies the full backup file.
			BackupRef string `json:"backupRef,omitempty"`

			// Specifies the time point the DB instance is restored to. At least one of the backupRef and restoreTime parameters should be specified. If both parameters are specified, the DB instance is restored using the full backup file.
			RestoreTime int64 `json:"restoreTime,omitempty"`

			// Specifies the source DB instance ID.
			SourceInstanceId string `json:"sourceInstanceId,omitempty"`
		} `json:"restorePoint,"`
	} `json:"instance,"`
}

type RestoreWithNewOptsBuilder interface {
	ToRestoreWithNewMap() (map[string]interface{}, error)
}

func (opts RestoreWithNewOpts) ToRestoreWithNewMap() (map[string]interface{}, error) {
	b, err := golangsdk.BuildRequestBody(opts, "")
	if err != nil {
		return nil, err
	}
	return b, nil
}

func RestoreWithNew(client *golangsdk.ServiceClient, versionId string, projectId string, opts RestoreWithNewOptsBuilder) (r RestoreWithNewResult) {
	b, _ := opts.ToRestoreWithNewMap()

	_, r.Err = client.Post(RestoreWithNewURL(client, versionId, projectId), b, &r.Body, &golangsdk.RequestOpts{
		OkCodes:     []int{200, 201, 202, 204},
		MoreHeaders: map[string]string{"X-Language": "en-us", "Content-Type": "application/json"},
	})
	return
}

repository: TonyGitUser/golangsdk

repo_name: TonyGitUser/golangsdk