func TestWipeoutBuildPathIfBuildOptionsChangedNoPreviousBuildOptions(t *testing.T) {
	ctx := &types.Context{}

	buildPath := SetupBuildPath(t, ctx)
	defer os.RemoveAll(buildPath)

	ctx.BuildOptionsJson = "{ \"new\":\"new\" }"

	utils.TouchFile(filepath.Join(buildPath, "should_not_be_deleted.txt"))

	commands := []types.Command{
		&builder.WipeoutBuildPathIfBuildOptionsChanged{},
	}

	for _, command := range commands {
		err := command.Run(ctx)
		NoError(t, err)
	}

	_, err := os.Stat(buildPath)
	NoError(t, err)

	files, err := gohasissues.ReadDir(buildPath)
	NoError(t, err)
	require.Equal(t, 1, len(files))

	_, err = os.Stat(filepath.Join(buildPath, "should_not_be_deleted.txt"))
	NoError(t, err)
}
func TestWipeoutBuildPathIfBuildOptionsChangedNoPreviousBuildOptions(t *testing.T) {
	context := make(map[string]interface{})

	buildPath := SetupBuildPath(t, context)
	defer os.RemoveAll(buildPath)

	context[constants.CTX_BUILD_OPTIONS_JSON] = "new"

	utils.TouchFile(filepath.Join(buildPath, "should_not_be_deleted.txt"))

	commands := []types.Command{
		&builder.SetupHumanLoggerIfMissing{},
		&builder.WipeoutBuildPathIfBuildOptionsChanged{},
	}

	for _, command := range commands {
		err := command.Run(context)
		NoError(t, err)
	}

	_, err := os.Stat(buildPath)
	NoError(t, err)

	files, err := gohasissues.ReadDir(buildPath)
	NoError(t, err)
	require.Equal(t, 1, len(files))

	_, err = os.Stat(filepath.Join(buildPath, "should_not_be_deleted.txt"))
	NoError(t, err)
}
func (s *WipeoutBuildPathIfBuildOptionsChanged) Run(context map[string]interface{}) error {
	if !utils.MapHas(context, constants.CTX_BUILD_OPTIONS_PREVIOUS_JSON) {
		return nil
	}

	buildOptionsJson := context[constants.CTX_BUILD_OPTIONS_JSON].(string)
	previousBuildOptionsJson := context[constants.CTX_BUILD_OPTIONS_PREVIOUS_JSON].(string)
	logger := context[constants.CTX_LOGGER].(i18n.Logger)

	if buildOptionsJson == previousBuildOptionsJson {
		return nil
	}

	logger.Println(constants.LOG_LEVEL_INFO, constants.MSG_BUILD_OPTIONS_CHANGED)

	buildPath := context[constants.CTX_BUILD_PATH].(string)
	files, err := gohasissues.ReadDir(buildPath)
	if err != nil {
		return utils.WrapError(err)
	}
	for _, file := range files {
		os.RemoveAll(filepath.Join(buildPath, file.Name()))
	}

	return nil
}
func TestCopyOtherFiles(t *testing.T) {
	context := make(map[string]interface{})

	buildPath := SetupBuildPath(t, context)
	defer os.RemoveAll(buildPath)

	context[constants.CTX_SKETCH_LOCATION] = filepath.Join("sketch1", "sketch.ino")

	commands := []types.Command{
		&builder.SetupHumanLoggerIfMissing{},
		&builder.AddAdditionalEntriesToContext{},
		&builder.SketchLoader{},
		&builder.AdditionalSketchFilesCopier{},
	}

	for _, command := range commands {
		err := command.Run(context)
		NoError(t, err)
	}

	_, err1 := os.Stat(filepath.Join(buildPath, constants.FOLDER_SKETCH, "header.h"))
	NoError(t, err1)

	files, err1 := gohasissues.ReadDir(filepath.Join(buildPath, constants.FOLDER_SKETCH))
	NoError(t, err1)
	require.Equal(t, 3, len(files))

	sort.Sort(ByFileInfoName(files))
	require.Equal(t, "header.h", files[0].Name())
	require.Equal(t, "s_file.S", files[1].Name())
	require.Equal(t, "subfolder", files[2].Name())

	files, err1 = gohasissues.ReadDir(filepath.Join(buildPath, constants.FOLDER_SKETCH, "subfolder"))
	NoError(t, err1)
	require.Equal(t, 1, len(files))
	require.Equal(t, "helper.h", files[0].Name())
}
func TestCopyOtherFiles(t *testing.T) {
	ctx := &types.Context{
		SketchLocation: filepath.Join("sketch1", "sketch.ino"),
	}

	buildPath := SetupBuildPath(t, ctx)
	defer os.RemoveAll(buildPath)

	commands := []types.Command{
		&builder.AddAdditionalEntriesToContext{},
		&builder.SketchLoader{},
		&builder.AdditionalSketchFilesCopier{},
	}

	for _, command := range commands {
		err := command.Run(ctx)
		NoError(t, err)
	}

	_, err1 := os.Stat(filepath.Join(buildPath, constants.FOLDER_SKETCH, "header.h"))
	NoError(t, err1)

	files, err1 := gohasissues.ReadDir(filepath.Join(buildPath, constants.FOLDER_SKETCH))
	NoError(t, err1)
	require.Equal(t, 3, len(files))

	sort.Sort(ByFileInfoName(files))
	require.Equal(t, "header.h", files[0].Name())
	require.Equal(t, "s_file.S", files[1].Name())
	require.Equal(t, "subfolder", files[2].Name())

	files, err1 = gohasissues.ReadDir(filepath.Join(buildPath, constants.FOLDER_SKETCH, "subfolder"))
	NoError(t, err1)
	require.Equal(t, 1, len(files))
	require.Equal(t, "helper.h", files[0].Name())
}
func downloadAndUnpack(url string) (string, []os.FileInfo, error) {
	fmt.Fprintln(os.Stderr, "Downloading "+url)

	unpackFolder, err := ioutil.TempDir(constants.EMPTY_STRING, "arduino-builder-tool")
	if err != nil {
		return constants.EMPTY_STRING, nil, i18n.WrapError(err)
	}

	urlParts := strings.Split(url, "/")
	archiveFileName := urlParts[len(urlParts)-1]
	archiveFilePath := filepath.Join(unpackFolder, archiveFileName)

	res, err := http.Get(url)
	if err != nil {
		return constants.EMPTY_STRING, nil, i18n.WrapError(err)
	}

	bytes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return constants.EMPTY_STRING, nil, i18n.WrapError(err)
	}
	res.Body.Close()

	utils.WriteFileBytes(archiveFilePath, bytes)

	cmd := buildUnpackCmd(archiveFilePath)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return constants.EMPTY_STRING, nil, i18n.WrapError(err)
	}
	if len(out) > 0 {
		fmt.Println(string(out))
	}

	os.Remove(archiveFilePath)

	files, err := gohasissues.ReadDir(unpackFolder)
	if err != nil {
		return constants.EMPTY_STRING, nil, i18n.WrapError(err)
	}

	return unpackFolder, files, nil
}
func (s *WipeoutBuildPathIfBuildOptionsChanged) Run(ctx *types.Context) error {
	if ctx.BuildOptionsJsonPrevious == "" {
		return nil
	}
	buildOptionsJson := ctx.BuildOptionsJson
	previousBuildOptionsJson := ctx.BuildOptionsJsonPrevious
	logger := ctx.GetLogger()

	var opts properties.Map
	var prevOpts properties.Map
	json.Unmarshal([]byte(buildOptionsJson), &opts)
	json.Unmarshal([]byte(previousBuildOptionsJson), &prevOpts)

	// If SketchLocation path is different but filename is the same, consider it equal
	if filepath.Base(opts["sketchLocation"]) == filepath.Base(prevOpts["sketchLocation"]) {
		delete(opts, "sketchLocation")
		delete(prevOpts, "sketchLocation")
	}

	if opts.Equals(prevOpts) {
		return nil
	}

	logger.Println(constants.LOG_LEVEL_INFO, constants.MSG_BUILD_OPTIONS_CHANGED)

	buildPath := ctx.BuildPath
	files, err := gohasissues.ReadDir(buildPath)
	if err != nil {
		return i18n.WrapError(err)
	}
	for _, file := range files {
		os.RemoveAll(filepath.Join(buildPath, file.Name()))
	}

	return nil
}