["// Copyright 2020 CIS Maxwell, LLC. All rights reserved.\n// Copyright 2020 The Calyx Institute\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"archive/zip\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"math\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar input string\n\nvar executable, _ = os.Executable()\nvar cwd = filepath.Dir(executable)\n\nvar adb *exec.Cmd\nvar fastboot *exec.Cmd\n\nvar platformToolsVersion = \"30.0.4\"\nvar platformToolsZip string\n\nvar deviceFactoryFolderMap map[string]string\n\n// Set via LDFLAGS, check Makefile\nvar version string\n\nconst OS = runtime.GOOS\n\nconst (\n\tUDEV_RULES = \"# Google\\nSUBSYSTEM==\\\"usb\\\", ATTR{idVendor}==\\\"18d1\\\", GROUP=\\\"sudo\\\"\\n# Xiaomi\\nSUBSYSTEM==\\\"usb\\\", ATTR{idVendor}==\\\"2717\\\", GROUP=\\\"sudo\\\"\\n\"\n\tRULES_FILE = \"98-device-flasher.rules\"\n\tRULES_PATH = \"/etc/udev/rules.d/\"\n)\n\nvar (\n\tError = Red\n\tWarn = Yellow\n)\n\nvar (\n\tBlue = Color(\"\\033[1;34m%s\\033[0m\")\n\tRed = Color(\"\\033[1;31m%s\\033[0m\")\n\tYellow = Color(\"\\033[1;33m%s\\033[0m\")\n)\n\nfunc Color(color string) func(...interface{}) string {\n\treturn func(args ...interface{}) string {\n\t\treturn fmt.Sprintf(color,\n\t\t\tfmt.Sprint(args...))\n\t}\n}\n\nfunc errorln(err interface{}, fatal bool) {\n\tlog, _ := os.OpenFile(\"error.log\", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)\n\t_, _ = fmt.Fprintln(log, err)\n\t_, _ = fmt.Fprintln(os.Stderr, Error(err))\n\tlog.Close()\n\tif fatal {\n\t\tcleanup()\n\t\tfmt.Println(\"Press enter to exit.\")\n\t\t_, _ = fmt.Scanln(&input)\n\t\tos.Exit(1)\n\t}\n}\n\nfunc warnln(warning interface{}) {\n\tfmt.Println(Warn(warning))\n}\n\nfunc cleanup() {\n\tif OS == \"linux\" {\n\t\t_, err := os.Stat(RULES_PATH + RULES_FILE)\n\t\tif !os.IsNotExist(err) {\n\t\t\t_ = exec.Command(\"sudo\", \"rm\", RULES_PATH+RULES_FILE).Run()\n\t\t}\n\t}\n}\n\nfunc main() {\n\tdefer cleanup()\n\t_ = os.Remove(\"error.log\")\n\tfmt.Println(\"Android Factory Image Flasher version \" + version)\n\t// Map device codenames to their corresponding extracted factory image folders\n\tdeviceFactoryFolderMap = getFactoryFolders()\n\tif len(deviceFactoryFolderMap) < 1 {\n\t\terrorln(errors.New(\"Cannot continue without a device factory image. Exiting...\"), true)\n\t}\n\terr := getPlatformTools()\n\tif err != nil {\n\t\terrorln(\"Cannot continue without Android platform tools. Exiting...\", false)\n\t\terrorln(err, true)\n\t}\n\tif OS == \"linux\" {\n\t\t// Linux weirdness\n\t\tcheckUdevRules()\n\t}\n\tplatformToolCommand := *adb\n\tplatformToolCommand.Args = append(adb.Args, \"start-server\")\n\terr = platformToolCommand.Run()\n\tif err != nil {\n\t\terrorln(\"Cannot start ADB server\", false)\n\t\terrorln(err, true)\n\t}\n\twarnln(\"1. Connect to a wifi network and ensure that no SIM cards are installed\")\n\twarnln(\"2. Enable Developer Options on device (Settings -> About Phone -> tap \\\"Build number\\\" 7 times)\")\n\twarnln(\"3. Enable USB debugging on device (Settings -> System -> Advanced -> Developer Options) and allow the computer to debug (hit \\\"OK\\\" on the popup when USB is connected)\")\n\twarnln(\"4. Enable OEM Unlocking (in the same Developer Options menu)\")\n\tfmt.Println()\n\tfmt.Print(Warn(\"Press ENTER to continue\"))\n\t_, _ = fmt.Scanln(&input)\n\tfmt.Println()\n\t// Map serial numbers to device codenames by extracting them from adb and fastboot command output\n\tdevices := getDevices()\n\tif len(devices) == 0 {\n\t\terrorln(errors.New(\"No devices to be flashed. Exiting...\"), true)\n\t} else if !PARALLEL && len(devices) > 1 {\n\t\terrorln(errors.New(\"More than one device detected. Exiting...\"), true)\n\t}\n\tfmt.Println()\n\tfmt.Println(\"Devices to be flashed: \")\n\tfor serialNumber, device := range devices {\n\t\tfmt.Println(device + \" \" + serialNumber)\n\t}\n\tfmt.Println()\n\tfmt.Print(Warn(\"Press ENTER to continue\"))\n\t_, _ = fmt.Scanln(&input)\n\t// Sequence: unlock bootloader -> execute flash-all script -> relock bootloader\n\tflashDevices(devices)\n}\n\nfunc getFactoryFolders() map[string]string {\n\tfiles, err := ioutil.ReadDir(cwd)\n\tif err != nil {\n\t\terrorln(err, true)\n\t}\n\tdeviceFactoryFolderMap := map[string]string{}\n\tfor _, file := range files {\n\t\tfile := file.Name()\n\t\tif strings.Contains(file, \"factory\") && strings.HasSuffix(file, \".zip\") {\n\t\t\tif strings.HasPrefix(file, \"jasmine\") {\n\t\t\t\tplatformToolsVersion = \"29.0.6\"\n\t\t\t}\n\t\t\textracted, err := extractZip(path.Base(file), cwd)\n\t\t\tif err != nil {\n\t\t\t\terrorln(\"Cannot continue without a factory image. Exiting...\", false)\n\t\t\t\terrorln(err, true)\n\t\t\t}\n\t\t\tdevice := strings.Split(file, \"-\")[0]\n\t\t\tif _, exists := deviceFactoryFolderMap[device]; !exists {\n\t\t\t\tdeviceFactoryFolderMap[device] = extracted[0]\n\t\t\t} else {\n\t\t\t\terrorln(\"More than one factory image available for \"+device, true)\n\t\t\t}\n\t\t}\n\t}\n\treturn deviceFactoryFolderMap\n}\n\nfunc getPlatformTools() error {\n\tplaformToolsUrlMap := map[[2]string]string{\n\t\t[2]string{\"darwin\", \"29.0.6\"}: \"https://dl.google.com/android/repository/platform-tools_r29.0.6-darwin.zip\",\n\t\t[2]string{\"linux\", \"29.0.6\"}: \"https://dl.google.com/android/repository/platform-tools_r29.0.6-linux.zip\",\n\t\t[2]string{\"windows\", \"29.0.6\"}: \"https://dl.google.com/android/repository/platform-tools_r29.0.6-windows.zip\",\n\t\t[2]string{\"darwin\", \"30.0.4\"}: \"https://dl.google.com/android/repository/fbad467867e935dce68a0296b00e6d1e76f15b15.platform-tools_r30.0.4-darwin.zip\",\n\t\t[2]string{\"linux\", \"30.0.4\"}: \"https://dl.google.com/android/repository/platform-tools_r30.0.4-linux.zip\",\n\t\t[2]string{\"windows\", \"30.0.4\"}: \"https://dl.google.com/android/repository/platform-tools_r30.0.4-windows.zip\",\n\t}\n\tplatformToolsChecksumMap := map[[2]string]string{\n\t\t[2]string{\"darwin\", \"29.0.6\"}: \"PI:FP:7555e8e24958cae4cfd197135950359b9fe8373d4862a03677f089d215119a3aEND_PI\",\n\t\t[2]string{\"linux\", \"29.0.6\"}: \"cc9e9d0224d1a917bad71fe12d209dfffe9ce43395e048ab2f07dcfc21101d44\",\n\t\t[2]string{\"windows\", \"29.0.6\"}: \"247210e3c12453545f8e1f76e55de3559c03f2d785487b2e4ac00fe9698a039c\",\n\t\t[2]string{\"darwin\", \"30.0.4\"}: \"e0db2bdc784c41847f854d6608e91597ebc3cef66686f647125f5a046068a890\",\n\t\t[2]string{\"linux\", \"30.0.4\"}: \"5be24ed897c7e061ba800bfa7b9ebb4b0f8958cc062f4b2202701e02f2725891\",\n\t\t[2]string{\"windows\", \"30.0.4\"}: \"413182fff6c5957911e231b9e97e6be4fc6a539035e3dfb580b5c54bd5950fee\",\n\t}\n\tplatformToolsOsVersion := [2]string{OS, platformToolsVersion}\n\t_, err := os.Stat(path.Base(plaformToolsUrlMap[platformToolsOsVersion]))\n\tif err != nil {\n\t\terr = downloadFile(plaformToolsUrlMap[platformToolsOsVersion])\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tplatformToolsZip = path.Base(plaformToolsUrlMap[platformToolsOsVersion])\n\terr = verifyZip(platformToolsZip, platformToolsChecksumMap[platformToolsOsVersion])\n\tif err != nil {\n\t\tfmt.Println(platformToolsZip + \" checksum verification failed\")\n\t\treturn err\n\t}\n\tplatformToolsPath := cwd + string(os.PathSeparator) + \"platform-tools\" + string(os.PathSeparator)\n\tpathEnvironmentVariable := func() string {\n\t\tif OS == \"windows\" {\n\t\t\treturn \"Path\"\n\t\t} else {\n\t\t\treturn \"PATH\"\n\t\t}\n\t}()\n\t_ = os.Setenv(pathEnvironmentVariable, platformToolsPath+string(os.PathListSeparator)+os.Getenv(pathEnvironmentVariable))\n\tadbPath := platformToolsPath + \"adb\"\n\tfastbootPath := platformToolsPath + \"fastboot\"\n\tif OS == \"windows\" {\n\t\tadbPath += \".exe\"\n\t\tfastbootPath += \".exe\"\n\t}\n\tadb = exec.Command(adbPath)\n\tfastboot = exec.Command(fastbootPath)\n\t// Ensure that no platform tools are running before attempting to overwrite them\n\tkillPlatformTools()\n\t_, err = extractZip(platformToolsZip, cwd)\n\treturn err\n}\n\nfunc checkUdevRules() {\n\t_, err := os.Stat(RULES_PATH)\n\tif os.IsNotExist(err) {\n\t\terr = exec.Command(\"sudo\", \"mkdir\", RULES_PATH).Run()\n\t\tif err != nil {\n\t\t\terrorln(\"Cannot continue without udev rules. Exiting...\", false)\n\t\t\terrorln(err, true)\n\t\t}\n\t}\n\t_, err = os.Stat(RULES_FILE)\n\tif os.IsNotExist(err) {\n\t\terr = ioutil.WriteFile(RULES_FILE, []byte(UDEV_RULES), 0644)\n\t\tif err != nil {\n\t\t\terrorln(\"Cannot continue without udev rules. Exiting...\", false)\n\t\t\terrorln(err, true)\n\t\t}\n\t\terr = exec.Command(\"sudo\", \"cp\", RULES_FILE, RULES_PATH).Run()\n\t\tif err != nil {\n\t\t\terrorln(\"Cannot continue without udev rules. Exiting...\", false)\n\t\t\terrorln(err, true)\n\t\t}\n\t\t_ = exec.Command(\"sudo\", \"udevadm\", \"control\", \"--reload-rules\").Run()\n\t\t_ = exec.Command(\"sudo\", \"udevadm\", \"trigger\").Run()\n\t}\n}\n\nfunc getDevices() map[string]string {\n\tdevices := map[string]string{}\n\tfor _, platformToolCommand := range []exec.Cmd{*adb, *fastboot} {\n\t\tplatformToolCommand.Args = append(platformToolCommand.Args, \"devices\")\n\t\toutput, _ := platformToolCommand.Output()\n\t\tlines := strings.Split(string(output), \"\\n\")\n\t\tif platformToolCommand.Path == adb.Path {\n\t\t\tlines = lines[1:]\n\t\t}\n\t\tfor i, device := range lines {\n\t\t\tif lines[i] != \"\" && lines[i] != \"\\r\" {\n\t\t\t\tserialNumber := strings.Split(device, \"\\t\")[0]\n\t\t\t\tif platformToolCommand.Path == adb.Path {\n\t\t\t\t\tdevice = getProp(\"ro.product.device\", serialNumber)\n\t\t\t\t} else if platformToolCommand.Path == fastboot.Path {\n\t\t\t\t\tdevice = getVar(\"product\", serialNumber)\n\t\t\t\t\tif device == \"jasmine\" {\n\t\t\t\t\t\tdevice += \"_sprout\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfmt.Print(\"Detected \" + device + \" \" + serialNumber)\n\t\t\t\tif _, ok := deviceFactoryFolderMap[device]; ok {\n\t\t\t\t\tdevices[serialNumber] = device\n\t\t\t\t\tfmt.Println()\n\t\t\t\t} else {\n\t\t\t\t\tfmt.Println(\". \" + \"No matching factory image found\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn devices\n}\n\nfunc getVar(prop string, device string) string {\n\tplatformToolCommand := *fastboot\n\tplatformToolCommand.Args = append(fastboot.Args, \"-s\", device, \"getvar\", prop)\n\tout, err := platformToolCommand.CombinedOutput()\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tlines := strings.Split(string(out), \"\\n\")\n\tfor _, line := range lines {\n\t\tif strings.Contains(line, prop) {\n\t\t\treturn strings.Trim(strings.Split(line, \" \")[1], \"\\r\")\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc getProp(prop string, device string) string {\n\tplatformToolCommand := *adb\n\tplatformToolCommand.Args = append(adb.Args, \"-s\", device, \"shell\", \"getprop\", prop)\n\tout, err := platformToolCommand.Output()\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn strings.Trim(string(out), \"[]\\n\\r\")\n}\n\nfunc flashDevices(devices map[string]string) {\n\tvar wg sync.WaitGroup\n\tfor serialNumber, device := range devices {\n\t\twg.Add(1)\n\t\tgo func(serialNumber, device string) {\n\t\t\tdefer wg.Done()\n\t\t\tplatformToolCommand := *adb\n\t\t\tplatformToolCommand.Args = append(platformToolCommand.Args, \"-s\", serialNumber, \"reboot\", \"bootloader\")\n\t\t\t_ = platformToolCommand.Run()\n\t\t\tfmt.Println(\"Unlocking \" + device + \" \" + serialNumber + \" bootloader...\")\n\t\t\twarnln(\"5. Please use the volume and power keys on the device to unlock the bootloader\")\n\t\t\tif device == \"jasmine\" || device == \"walleye\" {\n\t\t\t\tfmt.Println()\n\t\t\t\twarnln(\" 5a. Once \" + device + \" \" + serialNumber + \" boots, disconnect its cable and power it off\")\n\t\t\t\twarnln(\" 5b. Then, press volume down + power to boot it into fastboot mode, and connect the cable again.\")\n\t\t\t\tfmt.Println(\"The installation will resume automatically\")\n\t\t\t}\n\t\t\tfor i := 0; getVar(\"unlocked\", serialNumber) != \"yes\"; i++ {\n\t\t\t\tplatformToolCommand = *fastboot\n\t\t\t\tplatformToolCommand.Args = append(platformToolCommand.Args, \"-s\", serialNumber, \"flashing\", \"unlock\")\n\t\t\t\t_ = platformToolCommand.Start()\n\t\t\t\ttime.Sleep(30 * time.Second)\n\t\t\t\tif i >= 2 {\n\t\t\t\t\terrorln(\"Failed to unlock \"+device+\" \"+serialNumber+\" bootloader\", false)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tfmt.Println(\"Flashing \" + device + \" \" + serialNumber + \" bootloader...\")\n\t\t\tflashAll := exec.Command(\".\" + string(os.PathSeparator) + \"flash-all\" + func() string {\n\t\t\t\tif OS == \"windows\" {\n\t\t\t\t\treturn \".bat\"\n\t\t\t\t} else {\n\t\t\t\t\treturn \".sh\"\n\t\t\t\t}\n\t\t\t}())\n\t\t\tflashAll.Dir = deviceFactoryFolderMap[device]\n\t\t\tflashAll.Stderr = os.Stderr\n\t\t\terr := flashAll.Run()\n\t\t\tif err != nil {\n\t\t\t\terrorln(\"Failed to flash \"+device+\" \"+serialNumber, false)\n\t\t\t\terrorln(err.Error(), false)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfmt.Println(\"Locking \" + device + \" \" + serialNumber + \" bootloader...\")\n\t\t\twarnln(\"6. Please use the volume and power keys on the device to lock the bootloader\")\n\t\t\tif device == \"jasmine\" || device == \"walleye\" {\n\t\t\t\tfmt.Println()\n\t\t\t\twarnln(\" 6a. Once \" + device + \" \" + serialNumber + \" boots, disconnect its cable and power it off\")\n\t\t\t\twarnln(\" 6b. Then, press volume down + power to boot it into fastboot mode, and connect the cable again.\")\n\t\t\t\tfmt.Println(\"The installation will resume automatically\")\n\t\t\t}\n\t\t\tfor i := 0; getVar(\"unlocked\", serialNumber) != \"no\"; i++ {\n\t\t\t\tplatformToolCommand = *fastboot\n\t\t\t\tplatformToolCommand.Args = append(platformToolCommand.Args, \"-s\", serialNumber, \"flashing\", \"lock\")\n\t\t\t\t_ = platformToolCommand.Start()\n\t\t\t\ttime.Sleep(30 * time.Second)\n\t\t\t\tif i >= 2 {\n\t\t\t\t\terrorln(\"Failed to lock \"+device+\" \"+serialNumber+\" bootloader\", false)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tfmt.Println(\"Rebooting \" + device + \" \" + serialNumber + \"...\")\n\t\t\tplatformToolCommand = *fastboot\n\t\t\tplatformToolCommand.Args = append(platformToolCommand.Args, \"-s\", serialNumber, \"reboot\")\n\t\t\t_ = platformToolCommand.Start()\n\t\t\twarnln(\"7. Disable OEM unlocking from Developer Options after setting up your device\")\n\t\t}(serialNumber, device)\n\t}\n\twg.Wait()\n\tfmt.Println()\n\tfmt.Println(Blue(\"Flashing complete\"))\n}\n\nfunc killPlatformTools() {\n\t_, err := os.Stat(adb.Path)\n\tif err == nil {\n\t\tplatformToolCommand := *adb\n\t\tplatformToolCommand.Args = append(platformToolCommand.Args, \"kill-server\")\n\t\t_ = platformToolCommand.Run()\n\t}\n\tif OS == \"windows\" {\n\t\t_ = exec.Command(\"taskkill\", \"/IM\", \"fastboot.exe\", \"/F\").Run()\n\t}\n}\n\nfunc downloadFile(url string) error {\n\tfmt.Println(\"Downloading \" + url)\n\tresp, err := http.Get(url)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\n\tout, err := os.Create(path.Base(url))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer out.Close()\n\n\tcounter := &WriteCounter{}\n\t_, err = io.Copy(out, io.TeeReader(resp.Body, counter))\n\tfmt.Println()\n\treturn err\n}\n\nfunc extractZip(src string, destination string) ([]string, error) {\n\tfmt.Println(\"Extracting \" + src)\n\tvar filenames []string\n\tr, err := zip.OpenReader(src)\n\tif err != nil {\n\t\treturn filenames, err\n\t}\n\tdefer r.Close()\n\n\tfor _, f := range r.File {\n\t\tfpath := filepath.Join(destination, f.Name)\n\t\tif !strings.HasPrefix(fpath, filepath.Clean(destination)+string(os.PathSeparator)) {\n\t\t\treturn filenames, fmt.Errorf(\"%s is an illegal filepath\", fpath)\n\t\t}\n\t\tfilenames = append(filenames, fpath)\n\t\tif f.FileInfo().IsDir() {\n\t\t\tos.MkdirAll(fpath, os.ModePerm)\n\t\t\tcontinue\n\t\t}\n\t\tif err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {\n\t\t\treturn filenames, err\n\t\t}\n\t\toutFile, err := os.OpenFile(fpath,\n\t\t\tos.O_WRONLY|os.O_CREATE|os.O_TRUNC,\n\t\t\tf.Mode())\n\t\tif err != nil {\n\t\t\treturn filenames, err\n\t\t}\n\t\trc, err := f.Open()\n\t\tif err != nil {\n\t\t\treturn filenames, err\n\t\t}\n\t\t_, err = io.Copy(outFile, rc)\n\t\toutFile.Close()\n\t\trc.Close()\n\t\tif err != nil {\n\t\t\treturn filenames, err\n\t\t}\n\t}\n\treturn filenames, nil\n}\n\nfunc verifyZip(zipfile, sha256sum string) error {\n\tfmt.Println(\"Verifying \" + zipfile)\n\tf, err := os.Open(zipfile)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\n\th := sha256.New()\n\tif _, err := io.Copy(h, f); err != nil {\n\t\treturn err\n\t}\n\tsum := hex.EncodeToString(h.Sum(nil))\n\tif sha256sum == sum {\n\t\treturn nil\n\t}\n\treturn errors.New(\"sha256sum mismatch\")\n}\n\ntype WriteCounter struct {\n\tTotal uint64\n}\n\nfunc (wc *WriteCounter) Write(p []byte) (int, error) {\n\tn := len(p)\n\twc.Total += uint64(n)\n\twc.PrintProgress()\n\treturn n, nil\n}\n\nfunc (wc WriteCounter) PrintProgress() {\n\tfmt.Printf(\"\\r%s\", strings.Repeat(\" \", 35))\n\tfmt.Printf(\"\\rDownloading... %s downloaded\", Bytes(wc.Total))\n}\n\nfunc logn(n, b float64) float64 {\n\treturn math.Log(n) / math.Log(b)\n}\n\nfunc humanateBytes(s uint64, base float64, sizes []string) string {\n\tif s < 10 {\n\t\treturn fmt.Sprintf(\"%d B\", s)\n\t}\n\te := math.Floor(logn(float64(s), base))\n\tsuffix := sizes[int(e)]\n\tval := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10\n\tf := \"%.0f %s\"\n\tif val < 10 {\n\t\tf = \"%.1f %s\"\n\t}\n\n\treturn fmt.Sprintf(f, val, suffix)\n}\n\nfunc Bytes(s uint64) string {\n\tsizes := []string{\"B\", \"kB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\"}\n\treturn humanateBytes(s, 1000, sizes)\n}\n", "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.\n\"\"\"Centralized catalog of paths.\"\"\"\n\nimport os\n\n\nclass DatasetCatalog(object):\n DATA_DIR = \"./datasets\"\n DATASETS = {\n \"coco_2017_train\": {\n \"img_dir\": \"coco/train2017\",\n \"ann_file\": \"coco/annotations/instances_train2017.json\"\n },\n \"coco_2017_val\": {\n \"img_dir\": \"coco/val2017\",\n \"ann_file\": \"coco/annotations/instances_val2017.json\"\n },\n \"coco_2017_test_dev\": {\n \"img_dir\": \"coco/test2017\",\n \"ann_file\": \"coco/annotations/image_info_test-dev2017.json\"\n },\n \"coco_2014_train\": {\n \"img_dir\": \"coco/train2014\",\n \"ann_file\": \"coco/annotations/instances_train2014.json\"\n },\n \"coco_2014_val\": {\n \"img_dir\": \"coco/val2014\",\n \"ann_file\": \"coco/annotations/instances_val2014.json\"\n },\n \"coco_2014_minival\": {\n \"img_dir\": \"coco/val2014\",\n \"ann_file\": \"coco/annotations/instances_minival2014.json\"\n },\n \"coco_2014_valminusminival\": {\n \"img_dir\": \"coco/val2014\",\n \"ann_file\": \"coco/annotations/instances_valminusminival2014.json\"\n },\n \"keypoints_coco_2014_train\": {\n \"img_dir\": \"coco/train2014\",\n \"ann_file\": \"coco/annotations/person_keypoints_train2014.json\",\n },\n \"keypoints_coco_2014_val\": {\n \"img_dir\": \"coco/val2014\",\n \"ann_file\": \"coco/annotations/person_keypoints_val2014.json\"\n },\n \"keypoints_coco_2014_minival\": {\n \"img_dir\": \"coco/val2014\",\n \"ann_file\": \"coco/annotations/person_keypoints_minival2014.json\",\n },\n \"keypoints_coco_2014_valminusminival\": {\n \"img_dir\": \"coco/val2014\",\n \"ann_file\": \"coco/annotations/person_keypoints_valminusminival2014.json\",\n },\n \"voc_2007_train\": {\n \"data_dir\": \"voc/VOC2007\",\n \"split\": \"train\"\n },\n \"voc_2007_train_cocostyle\": {\n \"img_dir\": \"voc/VOC2007/JPEGImages\",\n \"ann_file\": \"voc/VOC2007/Annotations/pascal_train2007.json\"\n },\n \"voc_2007_val\": {\n \"data_dir\": \"voc/VOC2007\",\n \"split\": \"val\"\n },\n \"voc_2007_val_cocostyle\": {\n \"img_dir\": \"voc/VOC2007/JPEGImages\",\n \"ann_file\": \"voc/VOC2007/Annotations/pascal_val2007.json\"\n },\n \"voc_2007_test\": {\n \"data_dir\": \"voc/VOC2007\",\n \"split\": \"test\"\n },\n \"voc_2007_test_cocostyle\": {\n \"img_dir\": \"voc/VOC2007/JPEGImages\",\n \"ann_file\": \"voc/VOC2007/Annotations/pascal_test2007.json\"\n },\n \"voc_2012_train\": {\n \"data_dir\": \"voc/VOC2012\",\n \"split\": \"train\"\n },\n \"voc_2012_train_cocostyle\": {\n \"img_dir\": \"voc/VOC2012/JPEGImages\",\n \"ann_file\": \"voc/VOC2012/Annotations/pascal_train2012.json\"\n },\n \"voc_2012_val\": {\n \"data_dir\": \"voc/VOC2012\",\n \"split\": \"val\"\n },\n \"voc_2012_val_cocostyle\": {\n \"img_dir\": \"voc/VOC2012/JPEGImages\",\n \"ann_file\": \"voc/VOC2012/Annotations/pascal_val2012.json\"\n },\n \"voc_2012_test\": {\n \"data_dir\": \"voc/VOC2012\",\n \"split\": \"test\"\n # PASCAL VOC2012 doesn't made the test annotations available, so there's no json annotation\n },\n \"cityscapes_fine_instanceonly_seg_train_cocostyle\": {\n \"img_dir\": \"cityscapes/images\",\n \"ann_file\": \"cityscapes/annotations/instancesonly_filtered_gtFine_train.json\"\n },\n \"cityscapes_fine_instanceonly_seg_val_cocostyle\": {\n \"img_dir\": \"cityscapes/images\",\n \"ann_file\": \"cityscapes/annotations/instancesonly_filtered_gtFine_val.json\"\n },\n \"cityscapes_fine_instanceonly_seg_test_cocostyle\": {\n \"img_dir\": \"cityscapes/images\",\n \"ann_file\": \"cityscapes/annotations/instancesonly_filtered_gtFine_test.json\"\n }\n }\n\n @staticmethod\n def get(name):\n if \"coco\" in name:\n data_dir = DatasetCatalog.DATA_DIR\n attrs = DatasetCatalog.DATASETS[name]\n args = dict(\n root=os.path.join(data_dir, attrs[\"img_dir\"]),\n ann_file=os.path.join(data_dir, attrs[\"ann_file\"]),\n )\n return dict(\n factory=\"COCODataset\",\n args=args,\n )\n elif \"voc\" in name:\n data_dir = DatasetCatalog.DATA_DIR\n attrs = DatasetCatalog.DATASETS[name]\n args = dict(\n data_dir=os.path.join(data_dir, attrs[\"data_dir\"]),\n split=attrs[\"split\"],\n )\n return dict(\n factory=\"PascalVOCDataset\",\n args=args,\n )\n raise RuntimeError(\"Dataset not available: {}\".format(name))\n\n\nclass ModelCatalog(object):\n S3_C2_DETECTRON_URL = \"https://dl.fbaipublicfiles.com/detectron\"\n C2_IMAGENET_MODELS = {\n \"MSRA/R-50\": \"ImageNetPretrained/MSRA/R-50.pkl\",\n \"MSRA/R-50-GN\": \"ImageNetPretrained/47261647/R-50-GN.pkl\",\n \"MSRA/R-101\": \"ImageNetPretrained/MSRA/R-101.pkl\",\n \"MSRA/R-101-GN\": \"ImageNetPretrained/47592356/R-101-GN.pkl\",\n \"FAIR/20171220/X-101-32x8d\": \"ImageNetPretrained/20171220/X-101-32x8d.pkl\",\n \"FAIR/20171220/X-101-64x4d\": \"ImageNetPretrained/20171220/X-101-64x4d.pkl\",\n }\n\n C2_DETECTRON_SUFFIX = \"output/train/{}coco_2014_train%3A{}coco_2014_valminusminival/generalized_rcnn/model_final.pkl\"\n C2_DETECTRON_MODELS = {\n \"35857197/e2e_faster_rcnn_R-50-C4_1x\": \"01_33_49.iAX0mXvW\",\n \"35857345/e2e_faster_rcnn_R-50-FPN_1x\": \"01_36_30.cUF7QR7I\",\n \"35857890/e2e_faster_rcnn_R-101-FPN_1x\": \"01_38_50.sNxI7sX7\",\n \"36761737/e2e_faster_rcnn_X-101-32x8d-FPN_1x\": \"06_31_39.5MIHi1fZ\",\n \"35858791/e2e_mask_rcnn_R-50-C4_1x\": \"01_45_57.ZgkA7hPB\",\n \"35858933/e2e_mask_rcnn_R-50-FPN_1x\": \"01_48_14.DzEQe4wC\",\n \"35861795/e2e_mask_rcnn_R-101-FPN_1x\": \"02_31_37.KqyEK4tT\",\n \"36761843/e2e_mask_rcnn_X-101-32x8d-FPN_1x\": \"06_35_59.RZotkLKI\",\n \"37129812/e2e_mask_rcnn_X-152-32x8d-FPN-IN5k_1.44x\": \"09_35_36.8pzTQKYK\",\n # keypoints\n \"PI:FP:37697547/e2e_keypoint_rcnn_R-50-FPN_1xEND_PI\": \"08_42_54.kdzV35ao\"\n }\n\n @staticmethod\n def get(name):\n if name.startswith(\"Caffe2Detectron/COCO\"):\n return ModelCatalog.get_c2_detectron_12_2017_baselines(name)\n if name.startswith(\"ImageNetPretrained\"):\n return ModelCatalog.get_c2_imagenet_pretrained(name)\n raise RuntimeError(\"model not present in the catalog {}\".format(name))\n\n @staticmethod\n def get_c2_imagenet_pretrained(name):\n prefix = ModelCatalog.S3_C2_DETECTRON_URL\n name = name[len(\"ImageNetPretrained/\"):]\n name = ModelCatalog.C2_IMAGENET_MODELS[name]\n url = \"/\".join([prefix, name])\n return url\n\n @staticmethod\n def get_c2_detectron_12_2017_baselines(name):\n # Detectron C2 models are stored following the structure\n # prefix//2012_2017_baselines/.yaml./suffix\n # we use as identifiers in the catalog Caffe2Detectron/COCO//\n prefix = ModelCatalog.S3_C2_DETECTRON_URL\n dataset_tag = \"keypoints_\" if \"keypoint\" in name else \"\"\n suffix = ModelCatalog.C2_DETECTRON_SUFFIX.format(dataset_tag, dataset_tag)\n # remove identification prefix\n name = name[len(\"Caffe2Detectron/COCO/\"):]\n # split in and \n model_id, model_name = name.split(\"/\")\n # parsing to make it match the url address from the Caffe2 models\n model_name = \"{}.yaml\".format(model_name)\n signature = ModelCatalog.C2_DETECTRON_MODELS[name]\n unique_name = \".\".join([model_name, signature])\n url = \"/\".join([prefix, model_id, \"12_2017_baselines\", unique_name, suffix])\n return url\n", "# coding: utf-8\n#\n# Copyright 2014 The Oppia Authors. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, softwar\n# distributed under the License is distributed on an \"AS-IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom extensions.rich_text_components import base\n\n\nNONNEGATIVE_INT_SCHEMA = {\n 'type': 'int',\n 'validators': [{\n 'id': 'is_at_least',\n 'min_value': 0\n }],\n}\n\n\nclass Video(base.BaseRichTextComponent):\n \"\"\"A rich-text component representing a YouTube video.\"\"\"\n\n name = 'Video'\n category = 'Basic Input'\n description = 'A YouTube video.'\n frontend_name = 'video'\n tooltip = 'Insert video'\n\n _customization_arg_specs = [{\n 'name': 'video_id',\n 'description': (\n 'The YouTube id for this video. This is the 11-character string '\n 'after \\'v=\\' in the video URL.'),\n 'schema': {\n 'type': 'unicode',\n },\n 'default_value': '',\n }, {\n 'name': 'start',\n 'description': (\n 'Video start time in seconds: (leave at 0 to start at the '\n 'beginning.)'),\n 'schema': NONNEGATIVE_INT_SCHEMA,\n 'default_value': 0\n }, {\n 'name': 'end',\n 'description': (\n 'Video end time in seconds: (leave at 0 to play until the end.)'),\n 'schema': NONNEGATIVE_INT_SCHEMA,\n 'default_value': 0\n }, {\n 'name': 'autoplay',\n 'description': (\n 'Autoplay this video once the question has loaded?'),\n 'schema': {\n 'type': 'bool'\n },\n 'default_value': False,\n }]\n\n icon_data_url = (\n 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAA'\n 'ABGdBTUEAAK/INwWK6QAAABl0RVh0%0AU29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZ'\n 'TwAAAIfSURBVDjLpZNPaBNBGMXfbrubzBqbg4kL%0A0lJLgiVKE/AP6Kl6UUFQNAeDIAj'\n 'VS08aELx59GQPAREV/4BeiqcqROpRD4pUNCJSS21OgloISWME%0AZ/aPb6ARdNeTCz92m'\n 'O%2B9N9/w7RphGOJ/nsH%2Bolqtvg%2BCYJR8q9VquThxuVz%2BoJTKeZ63Uq/XC38E%0'\n 'A0Jj3ff8%2BOVupVGLbolkzQw5HOqAxQU4wXWWnZrykmYD0QsgAOJe9hpEUcPr8i0GaJ8'\n 'n2vs/sL2h8%0AR66TpVfWTdETHWE6GRGKjGiiKNLii5BSLpN7pBHpgMYhMkm8tPUWz3sL'\n '2D1wFaY/jvnWcTTaE5Dy%0AjMfTT5J0XIAiTRYn3ASwZ1MKbTmN7z%2BKaHUOYqmb1fcP'\n 'iNa4kQBuyvWAHYfcHGzDgYcx9NKrwJYH%0ACAyF21JiPWBnXMAQOea6bmn%2B4ueYGZi8'\n 'gtymNVobF7BG5prNpjd%2BeW6X4BSUD0gOdCpzA8MpA/v2%0Av15kl4%2BpK0emwHSbjJ'\n 'GBlz%2BvYM1fQeDrYOBTdzOGvDf6EFNr%2BLYjHbBgsaCLxr%2BmoNQjU2vYhRXp%0AgI'\n 'PI:FP:UOmSWWnsJRfjlOZhrexgtYDZ/gWbetNRbNs6QT10GJglNk64HMaGgbAkoMo5fiFNy7CKDEND_PI'\n 'QUGqE%0A5r38YktxAfSqW7Zt33l66WtkAkACjuNsaLVaDxlw5HdJ/86aYrG4WCgUZD6fX'\n '%2Bjv/U0ymfxoWVZo%0AmuZyf%2B8XqfGP49CCrBUAAAAASUVORK5CYII%3D%0A'\n )\n", "#\n# Copyright 2014 Google Inc. All rights reserved.\n#\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n# use this file except in compliance with the License. You may obtain a copy of\n# the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations under\n# the License.\n#\n\n\n\"\"\"Tests for client module.\"\"\"\n\nimport responses\nimport time\n\nimport googlemaps\nfrom googlemaps import client as _client\nimport test as _test\nimport requests\n\nclass ClientTest(_test.TestCase):\n\n def test_no_api_key(self):\n with self.assertRaises(Exception):\n client = googlemaps.Client()\n client.directions(\"Sydney\", \"Melbourne\")\n\n def test_invalid_api_key(self):\n with self.assertRaises(Exception):\n client = googlemaps.Client(key=\"Invalid key.\")\n client.directions(\"Sydney\", \"Melbourne\")\n\n def test_urlencode(self):\n # See GH #72.\n encoded_params = _client.urlencode_params([(\"address\", \"=Sydney ~\")])\n self.assertEqual(\"address=%3DSydney+~\", encoded_params)\n\n @responses.activate\n def test_queries_per_second(self):\n # This test assumes that the time to run a mocked query is\n # relatively small, eg a few milliseconds. We define a rate of\n # 3 queries per second, and run double that, which should take at\n # least 1 second but no more than 2.\n queries_per_second = 3\n query_range = range(queries_per_second * 2)\n for _ in query_range:\n responses.add(responses.GET,\n \"https://maps.googleapis.com/maps/api/geocode/json\",\n body='{\"status\":\"OK\",\"results\":[]}',\n status=200,\n content_type=\"application/json\")\n client = googlemaps.Client(key=\"AIzaasdf\",\n queries_per_second=queries_per_second)\n start = time.time()\n for _ in query_range:\n client.geocode(\"Sesame St.\")\n end = time.time()\n self.assertTrue(start + 1 < end < start + 2)\n\n @responses.activate\n def test_key_sent(self):\n responses.add(responses.GET,\n \"https://maps.googleapis.com/maps/api/geocode/json\",\n body='{\"status\":\"OK\",\"results\":[]}',\n status=200,\n content_type=\"application/json\")\n\n client = googlemaps.Client(key=\"AIzaasdf\")\n client.geocode(\"Sesame St.\")\n\n self.assertEqual(1, len(responses.calls))\n self.assertURLEqual(\"https://maps.googleapis.com/maps/api/geocode/json?\"\n \"key=AIzaasdf&address=Sesame+St.\",\n responses.calls[0].request.url)\n\n @responses.activate\n def test_extra_params(self):\n responses.add(responses.GET,\n \"https://maps.googleapis.com/maps/api/geocode/json\",\n body='{\"status\":\"OK\",\"results\":[]}',\n status=200,\n content_type=\"application/json\")\n\n client = googlemaps.Client(key=\"AIzaasdf\")\n client.geocode(\"Sesame St.\", extra_params={\"foo\": \"bar\"})\n\n self.assertEqual(1, len(responses.calls))\n self.assertURLEqual(\"https://maps.googleapis.com/maps/api/geocode/json?\"\n \"key=AIzaasdf&address=Sesame+St.&foo=bar\",\n responses.calls[0].request.url)\n\n def test_hmac(self):\n \"\"\"\n From http://en.wikipedia.org/wiki/Hash-based_message_authentication_code\n\n HMAC_SHA1(\"key\", \"The quick brown fox jumps over the lazy dog\")\n = 0xde7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9\n \"\"\"\n\n message = \"The quick brown fox jumps over the lazy dog\"\n key = \"a2V5\" # \"key\" -> base64\n signature = \"3nybhbi3iqa8ino29wqQcBydtNk=\"\n\n self.assertEqual(signature, _client.sign_hmac(key, message))\n\n @responses.activate\n def test_url_signed(self):\n responses.add(responses.GET,\n \"https://maps.googleapis.com/maps/api/geocode/json\",\n body='{\"status\":\"OK\",\"results\":[]}',\n status=200,\n content_type=\"application/json\")\n\n client = googlemaps.Client(client_id=\"foo\", client_secret=\"a2V5\")\n client.geocode(\"Sesame St.\")\n\n self.assertEqual(1, len(responses.calls))\n\n # Check ordering of parameters.\n self.assertIn(\"address=Sesame+St.&client=foo&signature\",\n responses.calls[0].request.url)\n self.assertURLEqual(\"https://maps.googleapis.com/maps/api/geocode/json?\"\n \"address=Sesame+St.&client=foo&\"\n \"PI:FP:signature=fxbWUIcNPZSekVOhp2ul9LW5TpY=END_PI\",\n responses.calls[0].request.url)\n\n @responses.activate\n def test_ua_sent(self):\n responses.add(responses.GET,\n \"https://maps.googleapis.com/maps/api/geocode/json\",\n body='{\"status\":\"OK\",\"results\":[]}',\n status=200,\n content_type=\"application/json\")\n\n client = googlemaps.Client(key=\"AIzaasdf\")\n client.geocode(\"Sesame St.\")\n\n self.assertEqual(1, len(responses.calls))\n user_agent = responses.calls[0].request.headers[\"User-Agent\"]\n self.assertTrue(user_agent.startswith(\"GoogleGeoApiClientPython\"))\n\n @responses.activate\n def test_retry(self):\n class request_callback:\n def __init__(self):\n self.first_req = True\n\n def __call__(self, req):\n if self.first_req:\n self.first_req = False\n return (200, {}, '{\"status\":\"OVER_QUERY_LIMIT\"}')\n return (200, {}, '{\"status\":\"OK\",\"results\":[]}')\n\n responses.add_callback(responses.GET,\n \"https://maps.googleapis.com/maps/api/geocode/json\",\n content_type='application/json',\n callback=request_callback())\n\n client = googlemaps.Client(key=\"AIzaasdf\")\n client.geocode(\"Sesame St.\")\n\n self.assertEqual(2, len(responses.calls))\n self.assertEqual(responses.calls[0].request.url, responses.calls[1].request.url)\n\n @responses.activate\n def test_transport_error(self):\n responses.add(responses.GET,\n \"https://maps.googleapis.com/maps/api/geocode/json\",\n status=404,\n content_type='application/json')\n\n client = googlemaps.Client(key=\"AIzaasdf\")\n with self.assertRaises(googlemaps.exceptions.HTTPError) as e:\n client.geocode(\"Foo\")\n\n self.assertEqual(e.exception.status_code, 404)\n\n @responses.activate\n def test_host_override(self):\n responses.add(responses.GET,\n \"https://foo.com/bar\",\n body='{\"status\":\"OK\",\"results\":[]}',\n status=200,\n content_type=\"application/json\")\n\n client = googlemaps.Client(key=\"AIzaasdf\")\n client._get(\"/bar\", {}, base_url=\"https://foo.com\")\n\n self.assertEqual(1, len(responses.calls))\n\n @responses.activate\n def test_custom_extract(self):\n def custom_extract(resp):\n return resp.json()\n\n responses.add(responses.GET,\n \"https://maps.googleapis.com/bar\",\n body='{\"error\":\"errormessage\"}',\n status=403,\n content_type=\"application/json\")\n\n client = googlemaps.Client(key=\"AIzaasdf\")\n b = client._get(\"/bar\", {}, extract_body=custom_extract)\n self.assertEqual(1, len(responses.calls))\n self.assertEqual(\"errormessage\", b[\"error\"])\n\n @responses.activate\n def test_retry_intermittent(self):\n class request_callback:\n def __init__(self):\n self.first_req = True\n\n def __call__(self, req):\n if self.first_req:\n self.first_req = False\n return (500, {}, 'Internal Server Error.')\n return (200, {}, '{\"status\":\"OK\",\"results\":[]}')\n\n responses.add_callback(responses.GET,\n \"https://maps.googleapis.com/maps/api/geocode/json\",\n content_type=\"application/json\",\n callback=request_callback())\n\n client = googlemaps.Client(key=\"AIzaasdf\")\n client.geocode(\"Sesame St.\")\n\n self.assertEqual(2, len(responses.calls))\n\n def test_channel_without_client_id(self):\n with self.assertRaises(ValueError):\n client = googlemaps.Client(key=\"AIzaasdf\", channel=\"mychannel\")\n\n def test_invalid_channel(self):\n # Cf. limitations here:\n # https://developers.google.com/maps/premium/reports\n # /usage-reports#channels\n with self.assertRaises(ValueError):\n client = googlemaps.Client(client_id=\"foo\", client_secret=\"a2V5\",\n channel=\"auieauie$? \")\n\n def test_auth_url_with_channel(self):\n client = googlemaps.Client(key=\"AIzaasdf\",\n client_id=\"foo\",\n client_secret=\"a2V5\",\n channel=\"MyChannel_1\")\n\n # Check ordering of parameters + signature.\n auth_url = client._generate_auth_url(\"/test\",\n {\"param\": \"param\"},\n accepts_clientid=True)\n self.assertEqual(auth_url, \"/test?param=param\"\n \"&channel=MyChannel_1\"\n \"&client=foo\"\n \"&signature=OH18GuQto_mEpxj99UimKskvo4k=\")\n\n # Check if added to requests to API with accepts_clientid=False\n auth_url = client._generate_auth_url(\"/test\",\n {\"param\": \"param\"},\n accepts_clientid=False)\n self.assertEqual(auth_url, \"/test?param=param&key=AIzaasdf\")\n\n def test_requests_version(self):\n client_args_timeout = {\n \"key\": \"AIzaasdf\",\n \"client_id\": \"foo\",\n \"client_secret\": \"a2V5\",\n \"channel\": \"MyChannel_1\",\n \"connect_timeout\": 5,\n \"read_timeout\": 5\n }\n client_args = client_args_timeout.copy()\n del client_args[\"connect_timeout\"]\n del client_args[\"read_timeout\"]\n\n requests.__version__ = '2.3.0'\n with self.assertRaises(NotImplementedError):\n googlemaps.Client(**client_args_timeout)\n googlemaps.Client(**client_args)\n\n requests.__version__ = '2.4.0'\n googlemaps.Client(**client_args_timeout)\n googlemaps.Client(**client_args)\n\n @responses.activate\n def test_no_retry_over_query_limit(self):\n responses.add(responses.GET,\n \"https://maps.googleapis.com/foo\",\n body='{\"status\":\"OVER_QUERY_LIMIT\"}',\n status=200,\n content_type=\"application/json\")\n\n client = googlemaps.Client(key=\"AIzaasdf\",\n retry_over_query_limit=False)\n\n with self.assertRaises(googlemaps.exceptions.ApiError):\n client._request(\"/foo\", {})\n\n self.assertEqual(1, len(responses.calls))\n", "require 'spec_helper'\nrequire 'yt/models/playlist_item'\n\ndescribe Yt::PlaylistItem, :device_app do\n subject(:item) { Yt::PlaylistItem.new id: id, auth: $account }\n\n context 'given an existing playlist item' do\n let(:id) { 'PI:FP:PLjW_GNR5Ir0GMlbJzA-aW0UV8TchJFb8p3uzrLNcZKPYEND_PI' }\n\n it 'returns valid metadata' do\n expect(item.title).to be_a String\n expect(item.description).to be_a String\n expect(item.thumbnail_url).to be_a String\n expect(item.published_at).to be_a Time\n expect(item.channel_id).to be_a String\n expect(item.channel_title).to be_a String\n expect(item.playlist_id).to be_a String\n expect(item.position).to be_an Integer\n expect(item.video_id).to be_a String\n expect(item.video).to be_a Yt::Video\n expect(item.privacy_status).to be_a String\n end\n end\n\n context 'given an unknown playlist item' do\n let(:id) { 'not-a-playlist-item-id' }\n\n it { expect{item.snippet}.to raise_error Yt::Errors::RequestError }\n end\n\n\n context 'given one of my own playlist items that I want to update' do\n before(:all) do\n @my_playlist = $account.create_playlist title: \"Yt Test Update Playlist Item #{rand}\"\n @my_playlist.add_video 'MESycYJytkU'\n @my_playlist_item = @my_playlist.add_video 'MESycYJytkU'\n end\n after(:all) { @my_playlist.delete }\n\n let(:id) { @my_playlist_item.id }\n let!(:old_title) { @my_playlist_item.title }\n let!(:old_privacy_status) { @my_playlist_item.privacy_status }\n let(:update) { @my_playlist_item.update attrs }\n\n context 'given I update the position' do\n let(:attrs) { {position: 0} }\n\n specify 'only updates the position' do\n expect(update).to be true\n expect(@my_playlist_item.position).to be 0\n expect(@my_playlist_item.title).to eq old_title\n expect(@my_playlist_item.privacy_status).to eq old_privacy_status\n end\n end\n end\nend"]