diff --git a/dvc/command/plots.py b/dvc/command/plots.py
index e4d007f60..f495a55b5 100644
--- a/dvc/command/plots.py
+++ b/dvc/command/plots.py
@@ -156,7 +156,7 @@ def add_parser(subparsers, parent_parser):
     _add_output_arguments(plots_diff_parser)
     plots_diff_parser.set_defaults(func=CmdPlotsDiff)
 
-    PLOTS_MODIFY_HELP = "Modify plot props associated with a target file."
+    PLOTS_MODIFY_HELP = "Modify plot properties associated with a target file."
     plots_modify_parser = plots_subparsers.add_parser(
         "modify",
         parents=[parent_parser],
@@ -165,11 +165,14 @@ def add_parser(subparsers, parent_parser):
         formatter_class=argparse.RawDescriptionHelpFormatter,
     )
     plots_modify_parser.add_argument(
-        "target", help="Plot file to set props to.",
+        "target", help="Plot file to set properties to.",
     )
     _add_props_arguments(plots_modify_parser)
     plots_modify_parser.add_argument(
-        "--unset", nargs="*", help="Props to unset.",
+        "--unset",
+        nargs="*",
+        metavar="<property>",
+        help="Properties to unset.",
     )
     plots_modify_parser.set_defaults(func=CmdPlotsModify)
 
@@ -196,9 +199,15 @@ def _add_props_arguments(parser):
         default=None,  # Use default None to distinguish when it's not used
         help="Provided CSV ot TSV datafile does not have a header.",
     )
-    parser.add_argument("--title", default=None, help="Plot title.")
-    parser.add_argument("--x-label", default=None, help="X axis label.")
-    parser.add_argument("--y-label", default=None, help="Y axis lebel.")
+    parser.add_argument(
+        "--title", default=None, metavar="<text>", help="Plot title."
+    )
+    parser.add_argument(
+        "--x-label", default=None, metavar="<text>", help="X axis label."
+    )
+    parser.add_argument(
+        "--y-label", default=None, metavar="<text>", help="Y axis lebel."
+    )
 
 
 def _add_output_arguments(parser):
diff --git a/dvc/dependency/__init__.py b/dvc/dependency/__init__.py
index 10a2f787a..1a778dbfc 100644
--- a/dvc/dependency/__init__.py
+++ b/dvc/dependency/__init__.py
@@ -80,6 +80,7 @@ def loadd_from(stage, d_list):
 
 
 def loads_from(stage, s_list, erepo=None):
+    assert isinstance(s_list, list)
     ret = []
     for s in s_list:
         info = {RepoDependency.PARAM_REPO: erepo} if erepo else {}
diff --git a/dvc/dvcfile.py b/dvc/dvcfile.py
index 298fe1bdf..df8a61b7c 100644
--- a/dvc/dvcfile.py
+++ b/dvc/dvcfile.py
@@ -5,8 +5,8 @@ import os
 from voluptuous import MultipleInvalid
 
 import dvc.prompt as prompt
-from dvc import serialize
 from dvc.exceptions import DvcException
+from dvc.stage import serialize
 from dvc.stage.exceptions import (
     StageFileAlreadyExistsError,
     StageFileBadNameError,
diff --git a/dvc/repo/plots/data.py b/dvc/repo/plots/data.py
index 68430e6a2..901548928 100644
--- a/dvc/repo/plots/data.py
+++ b/dvc/repo/plots/data.py
@@ -237,6 +237,10 @@ class YAMLPlotData(PlotData):
 
         return yaml.load(self.content, OrderedLoader)
 
+    def _processors(self):
+        parent_processors = super()._processors()
+        return [_find_data] + parent_processors
+
 
 def _load_from_revision(repo, datafile, revision):
     from dvc.repo.tree import RepoTree
diff --git a/dvc/stage/__init__.py b/dvc/stage/__init__.py
index 0728827e9..866203001 100644
--- a/dvc/stage/__init__.py
+++ b/dvc/stage/__init__.py
@@ -493,7 +493,11 @@ class Stage(params.StageParams):
             ret.append("changed checksum")
 
     def already_cached(self):
-        return self.deps_cached() and self.outs_cached()
+        return (
+            not self.changed_stage()
+            and self.deps_cached()
+            and self.outs_cached()
+        )
 
     def deps_cached(self):
         return all(not dep.changed() for dep in self.deps)
diff --git a/dvc/stage/cache.py b/dvc/stage/cache.py
index 8f26173bb..f20be749f 100644
--- a/dvc/stage/cache.py
+++ b/dvc/stage/cache.py
@@ -7,12 +7,13 @@ from funcy import first
 from voluptuous import Invalid
 
 from dvc.schema import COMPILED_LOCK_FILE_STAGE_SCHEMA
-from dvc.serialize import to_single_stage_lockfile
-from dvc.stage.loader import StageLoader
 from dvc.utils import dict_sha256, relpath
 from dvc.utils.fs import makedirs
 from dvc.utils.yaml import dump_yaml
 
+from .loader import StageLoader
+from .serialize import to_single_stage_lockfile
+
 logger = logging.getLogger(__name__)
 
 
@@ -78,7 +79,7 @@ class StageCache:
         return None
 
     def _create_stage(self, cache, wdir=None):
-        from dvc.stage import create_stage, PipelineStage
+        from . import create_stage, PipelineStage
 
         stage = create_stage(
             PipelineStage,
diff --git a/dvc/stage/imports.py b/dvc/stage/imports.py
index 49703c206..96a626e35 100644
--- a/dvc/stage/imports.py
+++ b/dvc/stage/imports.py
@@ -23,7 +23,7 @@ def sync_import(stage, dry=False, force=False):
     if dry:
         return
 
-    if not force and not stage.changed_stage() and stage.already_cached():
+    if not force and stage.already_cached():
         stage.outs[0].checkout()
     else:
         stage.deps[0].download(stage.outs[0])
diff --git a/dvc/stage/run.py b/dvc/stage/run.py
index 590863656..53bab38d2 100644
--- a/dvc/stage/run.py
+++ b/dvc/stage/run.py
@@ -82,7 +82,6 @@ def _is_cached(stage):
     cached = (
         not stage.is_callback
         and not stage.always_changed
-        and not stage.changed_stage()
         and stage.already_cached()
     )
     if cached:
diff --git a/dvc/serialize.py b/dvc/stage/serialize.py
similarity index 54%
rename from dvc/serialize.py
rename to dvc/stage/serialize.py
index 169bb7b50..cf80e399e 100644
--- a/dvc/serialize.py
+++ b/dvc/stage/serialize.py
@@ -3,15 +3,16 @@ from functools import partial
 from operator import attrgetter
 from typing import TYPE_CHECKING, List
 
-from funcy import lsplit, rpartial
+from funcy import post_processing
 
 from dvc.dependency import ParamsDependency
 from dvc.output import BaseOutput
-from dvc.stage.params import StageParams
-from dvc.stage.utils import resolve_wdir
 from dvc.utils.collections import apply_diff
 from dvc.utils.yaml import parse_yaml_for_update
 
+from .params import StageParams
+from .utils import resolve_wdir, split_params_deps
+
 if TYPE_CHECKING:
     from dvc.stage import PipelineStage, Stage
 
@@ -32,89 +33,96 @@ DEFAULT_PARAMS_FILE = ParamsDependency.DEFAULT_PARAMS_FILE
 sort_by_path = partial(sorted, key=attrgetter("def_path"))
 
 
-def _get_out(out):
-    res = OrderedDict()
+@post_processing(OrderedDict)
+def _get_flags(out):
     if not out.use_cache:
-        res[PARAM_CACHE] = False
+        yield PARAM_CACHE, False
     if out.persist:
-        res[PARAM_PERSIST] = True
+        yield PARAM_PERSIST, True
     if out.plot and isinstance(out.plot, dict):
-        res.update(out.plot)
-    return out.def_path if not res else {out.def_path: res}
+        # notice `out.plot` is not sorted
+        # `out.plot` is in the same order as is in the file when read
+        # and, should be dumped as-is without any sorting
+        yield from out.plot.items()
 
 
-def _get_outs(outs):
-    return [_get_out(out) for out in sort_by_path(outs)]
+def _serialize_out(out):
+    flags = _get_flags(out)
+    return out.def_path if not flags else {out.def_path: flags}
 
 
-def get_params_deps(stage: "PipelineStage"):
-    return lsplit(rpartial(isinstance, ParamsDependency), stage.deps)
+def _serialize_outs(outputs: List[BaseOutput]):
+    outs, metrics, plots = [], [], []
+    for out in sort_by_path(outputs):
+        bucket = outs
+        if out.plot:
+            bucket = plots
+        elif out.metric:
+            bucket = metrics
+        bucket.append(_serialize_out(out))
+    return outs, metrics, plots
 
 
-def _serialize_params(params: List[ParamsDependency]):
-    """Return two types of values from stage:
+def _serialize_params_keys(params):
+    """
+    Returns the following format of data:
+     ['lr', 'train', {'params2.yaml': ['lr']}]
 
-    `keys` - which is list of params without values, used in a pipeline file
+    The output is sorted, with keys of params from default params file being
+    at the first, and then followed by entry of other files in lexicographic
+    order. The keys of those custom files are also sorted in the same order.
+    """
+    keys = []
+    for param_dep in sort_by_path(params):
+        dump = param_dep.dumpd()
+        path, params = dump[PARAM_PATH], dump[PARAM_PARAMS]
+        assert isinstance(params, (dict, list))
+        # when on no_exec, params are not filled and are saved as list
+        k = sorted(params.keys() if isinstance(params, dict) else params)
+        if not k:
+            continue
 
-    which is in the shape of:
-        ['lr', 'train', {'params2.yaml': ['lr']}]
+        if path == DEFAULT_PARAMS_FILE:
+            keys = k + keys
+        else:
+            keys.append({path: k})
+    return keys
 
-    `key_vals` - which is list of params with values, used in a lockfile
-    which is in the shape of:
+
+def _serialize_params_values(params: List[ParamsDependency]):
+    """Returns output of following format, used for lockfile:
         {'params.yaml': {'lr': '1', 'train': 2}, {'params2.yaml': {'lr': '1'}}
+
+    Default params file are always kept at the start, followed by others in
+    alphabetical order. The param values are sorted too(not recursively though)
     """
-    keys = []
     key_vals = OrderedDict()
-
     for param_dep in sort_by_path(params):
         dump = param_dep.dumpd()
         path, params = dump[PARAM_PATH], dump[PARAM_PARAMS]
         if isinstance(params, dict):
-            k = list(params.keys())
-            if not k:
-                continue
-            key_vals[path] = OrderedDict(
-                [(key, params[key]) for key in sorted(k)]
-            )
-        else:
-            assert isinstance(params, list)
-            k = params
-            key_vals = OrderedDict()
-        # params from default file is always kept at the start of the `params:`
-        if path == DEFAULT_PARAMS_FILE:
-            keys = k + keys
-            if key_vals:
+            kv = [(key, params[key]) for key in sorted(params.keys())]
+            key_vals[path] = OrderedDict(kv)
+            if path == DEFAULT_PARAMS_FILE:
                 key_vals.move_to_end(path, last=False)
-        else:
-            # if it's not a default file, change the shape
-            # to: {path: k}
-            keys.append({path: k})
-    return keys, key_vals
+    return key_vals
 
 
 def to_pipeline_file(stage: "PipelineStage"):
-    params, deps = get_params_deps(stage)
-    serialized_params, _ = _serialize_params(params)
+    wdir = resolve_wdir(stage.wdir, stage.path)
+    params, deps = split_params_deps(stage)
+    deps = sorted([d.def_path for d in deps])
+    params = _serialize_params_keys(params)
 
+    outs, metrics, plots = _serialize_outs(stage.outs)
     res = [
         (stage.PARAM_CMD, stage.cmd),
-        (stage.PARAM_WDIR, resolve_wdir(stage.wdir, stage.path)),
-        (stage.PARAM_DEPS, sorted([d.def_path for d in deps])),
-        (stage.PARAM_PARAMS, serialized_params),
-        (
-            PARAM_OUTS,
-            _get_outs(
-                [out for out in stage.outs if not (out.metric or out.plot)]
-            ),
-        ),
-        (
-            stage.PARAM_METRICS,
-            _get_outs([out for out in stage.outs if out.metric]),
-        ),
-        (
-            stage.PARAM_PLOTS,
-            _get_outs([out for out in stage.outs if out.plot]),
-        ),
+        (stage.PARAM_WDIR, wdir),
+        (stage.PARAM_DEPS, deps),
+        (stage.PARAM_PARAMS, params),
+        (stage.PARAM_OUTS, outs),
+        (stage.PARAM_METRICS, metrics),
+        (stage.PARAM_PLOTS, plots),
         (stage.PARAM_FROZEN, stage.frozen),
         (stage.PARAM_ALWAYS_CHANGED, stage.always_changed),
     ]
@@ -127,7 +135,7 @@ def to_single_stage_lockfile(stage: "Stage") -> dict:
     assert stage.cmd
 
     res = OrderedDict([("cmd", stage.cmd)])
-    params, deps = get_params_deps(stage)
+    params, deps = split_params_deps(stage)
     deps, outs = [
         [
             OrderedDict(
@@ -140,10 +148,11 @@ def to_single_stage_lockfile(stage: "Stage") -> dict:
         ]
         for items in [deps, stage.outs]
     ]
+    params = _serialize_params_values(params)
     if deps:
         res[PARAM_DEPS] = deps
     if params:
-        _, res[PARAM_PARAMS] = _serialize_params(params)
+        res[PARAM_PARAMS] = params
     if outs:
         res[PARAM_OUTS] = outs
 
diff --git a/dvc/stage/utils.py b/dvc/stage/utils.py
index 34cb879af..939803e8b 100644
--- a/dvc/stage/utils.py
+++ b/dvc/stage/utils.py
@@ -2,9 +2,12 @@ import os
 import pathlib
 from itertools import product
 
+from funcy import lsplit, rpartial
+
 from dvc import dependency, output
 from dvc.utils.fs import path_isin
 
+from ..dependency import ParamsDependency
 from ..remote import LocalRemote, S3Remote
 from ..utils import dict_md5, format_link, relpath
 from .exceptions import (
@@ -196,3 +199,7 @@ def get_dump(stage):
         }.items()
         if value
     }
+
+
+def split_params_deps(stage):
+    return lsplit(rpartial(isinstance, ParamsDependency), stage.deps)
diff --git a/setup.cfg b/setup.cfg
index f756962f0..fbcef890d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -14,4 +14,6 @@ include_trailing_comma=true
 known_first_party=dvc,tests
 known_third_party=PyInstaller,RangeHTTPServer,boto3,colorama,configobj,distro,dpath,flaky,flufl,funcy,git,google,grandalf,mock,mockssh,moto,nanotime,networkx,packaging,paramiko,pathspec,pytest,requests,ruamel,setuptools,shortuuid,tqdm,voluptuous,yaml,zc
 line_length=79
+force_grid_wrap=0
+use_parentheses=True
 multi_line_output=3

plots: support yaml
DVC supports JSON and YAML for metrics, but not for plots.

Details: https://github.com/iterative/dvc.org/pull/1382#discussion_r434967192

---
UPDATE by Jorge:

- [ ] Let's remember to update docs when this is figured out 🙂 Either in iterative/dvc.org/pull/1382 or in it's own PR on top of those changes.
diff --git a/tests/func/plots/test_plots.py b/tests/func/plots/test_plots.py
index 75835e636..cbd0afc07 100644
--- a/tests/func/plots/test_plots.py
+++ b/tests/func/plots/test_plots.py
@@ -10,9 +10,11 @@ import yaml
 from funcy import first
 
 from dvc.repo.plots.data import (
+    JSONPlotData,
     NoMetricInHistoryError,
     PlotData,
     PlotMetricTypeError,
+    YAMLPlotData,
 )
 from dvc.repo.plots.template import (
     NoDataForTemplateError,
@@ -581,3 +583,29 @@ def test_raise_on_wrong_field(tmp_dir, scm, dvc, run_copy_metrics):
 
     with pytest.raises(NoFieldInDataError):
         dvc.plots.show("metric.json", props={"y": "no_val"})
+
+
+def test_load_metric_from_dict_json(tmp_dir):
+    metric = [{"acccuracy": 1, "loss": 2}, {"accuracy": 3, "loss": 4}]
+    dmetric = {"train": metric}
+
+    plot_data = JSONPlotData("-", "revision", json.dumps(dmetric))
+
+    expected = metric
+    for d in expected:
+        d["rev"] = "revision"
+
+    assert list(map(dict, plot_data.to_datapoints())) == expected
+
+
+def test_load_metric_from_dict_yaml(tmp_dir):
+    metric = [{"acccuracy": 1, "loss": 2}, {"accuracy": 3, "loss": 4}]
+    dmetric = {"train": metric}
+
+    plot_data = YAMLPlotData("-", "revision", yaml.dump(dmetric))
+
+    expected = metric
+    for d in expected:
+        d["rev"] = "revision"
+
+    assert list(map(dict, plot_data.to_datapoints())) == expected
diff --git a/tests/func/test_lockfile.py b/tests/func/test_lockfile.py
index 87b3ebb00..2d2231368 100644
--- a/tests/func/test_lockfile.py
+++ b/tests/func/test_lockfile.py
@@ -4,7 +4,7 @@ from operator import itemgetter
 import yaml
 
 from dvc.dvcfile import PIPELINE_LOCK
-from dvc.serialize import get_params_deps
+from dvc.stage.utils import split_params_deps
 from dvc.utils.fs import remove
 from dvc.utils.yaml import parse_yaml_for_update
 from tests.func.test_run_multistage import supported_params
@@ -137,7 +137,7 @@ def test_params_dump(tmp_dir, dvc, run_head):
     assert not dvc.reproduce(stage.addressing)
 
     # let's change the order of params and dump them in pipeline file
-    params, _ = get_params_deps(stage)
+    params, _ = split_params_deps(stage)
     for param in params:
         param.params.reverse()
 
diff --git a/tests/func/test_repro_multistage.py b/tests/func/test_repro_multistage.py
index c26f3ad62..0507aacbc 100644
--- a/tests/func/test_repro_multistage.py
+++ b/tests/func/test_repro_multistage.py
@@ -509,7 +509,7 @@ def test_cyclic_graph_error(tmp_dir, dvc, run_copy):
 def test_repro_multiple_params(tmp_dir, dvc):
     from tests.func.test_run_multistage import supported_params
 
-    from dvc.serialize import get_params_deps
+    from dvc.stage.utils import split_params_deps
 
     with (tmp_dir / "params2.yaml").open("w+") as f:
         yaml.dump(supported_params, f)
@@ -529,7 +529,7 @@ def test_repro_multiple_params(tmp_dir, dvc):
         cmd="cat params2.yaml params.yaml > bar",
     )
 
-    params, deps = get_params_deps(stage)
+    params, deps = split_params_deps(stage)
     assert len(params) == 2
     assert len(deps) == 1
     assert len(stage.outs) == 1
diff --git a/tests/unit/stage/test_loader_pipeline_file.py b/tests/unit/stage/test_loader_pipeline_file.py
index 5ee89c661..264576c86 100644
--- a/tests/unit/stage/test_loader_pipeline_file.py
+++ b/tests/unit/stage/test_loader_pipeline_file.py
@@ -5,9 +5,9 @@ from itertools import chain
 import pytest
 
 from dvc.dvcfile import PIPELINE_FILE, Dvcfile
-from dvc.serialize import get_params_deps
 from dvc.stage import PipelineStage, create_stage
 from dvc.stage.loader import StageLoader
+from dvc.stage.serialize import split_params_deps
 
 
 @pytest.fixture
@@ -61,7 +61,7 @@ def test_fill_from_lock_params(dvc, lock_data):
             "ipsum": "ipsum"
         },
     }
-    params_deps = get_params_deps(stage)[0]
+    params_deps = split_params_deps(stage)[0]
     assert set(params_deps[0].params) == {"lorem", "lorem.ipsum"}
     assert set(params_deps[1].params) == {"ipsum", "foobar"}
     assert not params_deps[0].info
@@ -81,7 +81,7 @@ def test_fill_from_lock_missing_params_section(dvc, lock_data):
         outs=["bar"],
         params=["lorem", "lorem.ipsum", {"myparams.yaml": ["ipsum"]}],
     )
-    params_deps = get_params_deps(stage)[0]
+    params_deps = split_params_deps(stage)[0]
     StageLoader.fill_from_lock(stage, lock_data)
     assert not params_deps[0].info and not params_deps[1].info
 
@@ -180,7 +180,7 @@ def test_load_stage_with_params(dvc, stage_data, lock_data):
     dvcfile = Dvcfile(dvc, PIPELINE_FILE)
     stage = StageLoader.load_stage(dvcfile, "stage-1", stage_data, lock_data)
 
-    params, deps = get_params_deps(stage)
+    params, deps = split_params_deps(stage)
     assert deps[0].def_path == "foo" and stage.outs[0].def_path == "bar"
     assert params[0].def_path == "params.yaml"
     assert params[0].info == {"lorem": "ipsum"}
diff --git a/tests/unit/stage/test_serialize_pipeline_file.py b/tests/unit/stage/test_serialize_pipeline_file.py
new file mode 100644
index 000000000..113591bd0
--- /dev/null
+++ b/tests/unit/stage/test_serialize_pipeline_file.py
@@ -0,0 +1,196 @@
+import os
+
+import pytest
+from voluptuous import Schema as _Schema
+
+from dvc import output
+from dvc.dvcfile import PIPELINE_FILE
+from dvc.schema import SINGLE_PIPELINE_STAGE_SCHEMA
+from dvc.stage import PipelineStage, create_stage
+from dvc.stage.serialize import to_pipeline_file as _to_pipeline_file
+
+kwargs = {"name": "something", "cmd": "command", "path": PIPELINE_FILE}
+Schema = _Schema(SINGLE_PIPELINE_STAGE_SCHEMA)
+
+
+def to_pipeline_file(stage):
+    """Validate schema on each serialization."""
+    e = _to_pipeline_file(stage)
+    assert len(Schema(e)) == 1
+    return e
+
+
+def test_cmd(dvc):
+    stage = create_stage(PipelineStage, dvc, **kwargs)
+    entry = to_pipeline_file(stage)
+    assert entry == {"something": {"cmd": "command"}}
+
+
+def test_wdir(dvc):
+    stage = create_stage(PipelineStage, dvc, **kwargs)
+    assert stage.PARAM_WDIR not in to_pipeline_file(stage)["something"]
+
+    stage.wdir = os.curdir
+    assert stage.PARAM_WDIR not in to_pipeline_file(stage)["something"]
+
+    stage.wdir = "some-dir"
+    assert to_pipeline_file(stage)["something"][stage.PARAM_WDIR] == "some-dir"
+
+
+def test_deps_sorted(dvc):
+    stage = create_stage(
+        PipelineStage, dvc, deps=["a", "quick", "lazy", "fox"], **kwargs
+    )
+    assert to_pipeline_file(stage)["something"][stage.PARAM_DEPS] == [
+        "a",
+        "fox",
+        "lazy",
+        "quick",
+    ]
+
+
+def test_outs_sorted(dvc):
+    stage = create_stage(
+        PipelineStage,
+        dvc,
+        outs=["too", "many", "outs"],
+        deps=["foo"],
+        **kwargs,
+    )
+    assert to_pipeline_file(stage)["something"][stage.PARAM_OUTS] == [
+        "many",
+        "outs",
+        "too",
+    ]
+
+
+def test_params_sorted(dvc):
+    params = [
+        "lorem",
+        "ipsum",
+        {"custom.yaml": ["wxyz", "pqrs", "baz"]},
+        {"params.yaml": ["barr"]},
+    ]
+    stage = create_stage(
+        PipelineStage, dvc, outs=["bar"], deps=["foo"], params=params, **kwargs
+    )
+    assert to_pipeline_file(stage)["something"][stage.PARAM_PARAMS] == [
+        "barr",
+        "ipsum",
+        "lorem",
+        {"custom.yaml": ["baz", "pqrs", "wxyz"]},
+    ]
+
+
+def test_params_file_sorted(dvc):
+    params = [
+        "lorem",
+        "ipsum",
+        {"custom.yaml": ["wxyz", "pqrs", "baz"]},
+        {"a-file-of-params.yaml": ["barr"]},
+    ]
+    stage = create_stage(
+        PipelineStage, dvc, outs=["bar"], deps=["foo"], params=params, **kwargs
+    )
+    assert to_pipeline_file(stage)["something"][stage.PARAM_PARAMS] == [
+        "ipsum",
+        "lorem",
+        {"a-file-of-params.yaml": ["barr"]},
+        {"custom.yaml": ["baz", "pqrs", "wxyz"]},
+    ]
+
+
+@pytest.mark.parametrize(
+    "typ, extra",
+    [("plots", {"plot": True}), ("metrics", {"metric": True}), ("outs", {})],
+)
+def test_outs_and_outs_flags_are_sorted(dvc, typ, extra):
+    stage = create_stage(PipelineStage, dvc, deps=["input"], **kwargs)
+    stage.outs += output.loads_from(stage, ["barr"], use_cache=False, **extra)
+    stage.outs += output.loads_from(
+        stage, ["foobar"], use_cache=False, persist=True, **extra
+    )
+    stage.outs += output.loads_from(stage, ["foo"], persist=True, **extra)
+    stage.outs += output.loads_from(stage, ["bar"], **extra)
+
+    serialized_outs = to_pipeline_file(stage)["something"][typ]
+    assert serialized_outs == [
+        "bar",
+        {"barr": {"cache": False}},
+        {"foo": {"persist": True}},
+        {"foobar": {"cache": False, "persist": True}},
+    ]
+    assert list(serialized_outs[3]["foobar"].keys()) == ["cache", "persist"]
+
+
+def test_plot_props(dvc):
+    props = {"x": "1"}
+    stage = create_stage(PipelineStage, dvc, plots=["plot_file"], **kwargs)
+    stage.outs[0].plot = props
+
+    assert to_pipeline_file(stage)["something"][stage.PARAM_PLOTS] == [
+        {"plot_file": props}
+    ]
+
+
+def test_frozen(dvc):
+    stage = create_stage(
+        PipelineStage, dvc, outs=["output"], deps=["input"], **kwargs
+    )
+    assert stage.PARAM_FROZEN not in to_pipeline_file(stage)["something"]
+
+    stage = create_stage(PipelineStage, dvc, **kwargs, frozen=True)
+    assert to_pipeline_file(stage)["something"][stage.PARAM_FROZEN] is True
+
+
+def test_always_changed(dvc):
+    stage = create_stage(
+        PipelineStage, dvc, outs=["output"], deps=["input"], **kwargs
+    )
+    assert (
+        stage.PARAM_ALWAYS_CHANGED not in to_pipeline_file(stage)["something"]
+    )
+
+    stage = create_stage(PipelineStage, dvc, **kwargs, always_changed=True)
+    assert (
+        to_pipeline_file(stage)["something"][stage.PARAM_ALWAYS_CHANGED]
+        is True
+    )
+
+
+def test_order(dvc):
+    stage = create_stage(
+        PipelineStage,
+        dvc,
+        outs=["output"],
+        deps=["input"],
+        **kwargs,
+        always_changed=True,
+        frozen=True,
+    )
+    # `create_stage` checks for existence of `wdir`
+    stage.wdir = "some-dir"
+    assert list(to_pipeline_file(stage)["something"].keys()) == [
+        "cmd",
+        "wdir",
+        "deps",
+        "outs",
+        "frozen",
+        "always_changed",
+    ]
+
+
+@pytest.mark.parametrize(
+    "typ", ["outs", "metrics", "plots", "params", "deps", None]
+)
+def test_order_deps_outs(dvc, typ):
+    all_types = ["deps", "params", "outs", "metrics", "plots"]
+    all_types = [item for item in all_types if item != typ]
+    extra = {key: [f"foo-{i}"] for i, key in enumerate(all_types)}
+
+    stage = create_stage(PipelineStage, dvc, **kwargs, **extra)
+    assert typ not in to_pipeline_file(stage)["something"]
+    assert (
+        list(to_pipeline_file(stage)["something"].keys())
+        == ["cmd"] + all_types
+    )
diff --git a/tests/unit/stage/test_serialize_pipeline_lock.py b/tests/unit/stage/test_serialize_pipeline_lock.py
new file mode 100644
index 000000000..85721e07c
--- /dev/null
+++ b/tests/unit/stage/test_serialize_pipeline_lock.py
@@ -0,0 +1,208 @@
+from collections import OrderedDict
+
+import pytest
+from voluptuous import Schema as _Schema
+
+from dvc.dvcfile import PIPELINE_FILE
+from dvc.schema import LOCK_FILE_STAGE_SCHEMA, LOCKFILE_SCHEMA
+from dvc.stage import PipelineStage, create_stage
+from dvc.stage.serialize import DEFAULT_PARAMS_FILE, to_lockfile
+from dvc.stage.serialize import (
+    to_single_stage_lockfile as _to_single_stage_lockfile,
+)
+from dvc.stage.utils import split_params_deps
+
+kwargs = {"name": "something", "cmd": "command", "path": PIPELINE_FILE}
+Schema = _Schema(LOCK_FILE_STAGE_SCHEMA)
+
+
+def to_single_stage_lockfile(stage):
+    """Validate schema on each serialization."""
+    e = _to_single_stage_lockfile(stage)
+    assert Schema(e)
+    return e
+
+
+def test_lock(dvc):
+    stage = create_stage(PipelineStage, dvc, **kwargs)
+    assert to_single_stage_lockfile(stage) == {"cmd": "command"}
+
+
+def test_lock_deps(dvc):
+    stage = create_stage(PipelineStage, dvc, deps=["input"], **kwargs)
+    stage.deps[0].info = {"md5": "md-five"}
+    assert to_single_stage_lockfile(stage) == OrderedDict(
+        [
+            ("cmd", "command"),
+            ("deps", [OrderedDict([("path", "input"), ("md5", "md-five")])]),
+        ]
+    )
+
+
+def test_lock_deps_order(dvc):
+    stage = create_stage(
+        PipelineStage, dvc, deps=["input1", "input0"], **kwargs
+    )
+    stage.deps[0].info = {"md5": "md-one1"}
+    stage.deps[1].info = {"md5": "md-zer0"}
+    assert to_single_stage_lockfile(stage) == OrderedDict(
+        [
+            ("cmd", "command"),
+            (
+                "deps",
+                [
+                    OrderedDict([("path", "input0"), ("md5", "md-zer0")]),
+                    OrderedDict([("path", "input1"), ("md5", "md-one1")]),
+                ],
+            ),
+        ]
+    )
+
+
+def test_lock_params(dvc):
+    stage = create_stage(
+        PipelineStage, dvc, params=["lorem.ipsum", "abc"], **kwargs
+    )
+    stage.deps[0].info = {"lorem.ipsum": {"lorem1": 1, "lorem2": 2}, "abc": 3}
+    assert to_single_stage_lockfile(stage)["params"][
+        DEFAULT_PARAMS_FILE
+    ] == OrderedDict([("abc", 3), ("lorem.ipsum", {"lorem1": 1, "lorem2": 2})])
+
+
+def test_lock_params_file_sorted(dvc):
+    stage = create_stage(
+        PipelineStage,
+        dvc,
+        params=[
+            "lorem.ipsum",
+            "abc",
+            {"myparams.yaml": ["foo", "foobar"]},
+            {"a-params-file.yaml": ["bar", "barr"]},
+        ],
+        **kwargs
+    )
+    stage.deps[0].info = {"lorem.ipsum": {"lorem1": 1, "lorem2": 2}, "abc": 3}
+    stage.deps[1].info = {"foo": ["f", "o", "o"], "foobar": "foobar"}
+    stage.deps[2].info = {"bar": ["b", "a", "r"], "barr": "barr"}
+    assert to_single_stage_lockfile(stage)["params"] == OrderedDict(
+        [
+            (
+                DEFAULT_PARAMS_FILE,
+                OrderedDict(
+                    [("abc", 3), ("lorem.ipsum", {"lorem1": 1, "lorem2": 2})]
+                ),
+            ),
+            (
+                "a-params-file.yaml",
+                OrderedDict([("bar", ["b", "a", "r"]), ("barr", "barr")]),
+            ),
+            (
+                "myparams.yaml",
+                OrderedDict([("foo", ["f", "o", "o"]), ("foobar", "foobar")]),
+            ),
+        ]
+    )
+
+
+def test_lock_params_no_values_filled(dvc):
+    stage = create_stage(
+        PipelineStage, dvc, params=["lorem.ipsum", "abc"], **kwargs
+    )
+    assert to_single_stage_lockfile(stage) == {"cmd": "command"}
+
+
+@pytest.mark.parametrize("typ", ["plots", "metrics", "outs"])
+def test_lock_outs(dvc, typ):
+    stage = create_stage(PipelineStage, dvc, **{typ: ["input"]}, **kwargs)
+    stage.outs[0].info = {"md5": "md-five"}
+    assert to_single_stage_lockfile(stage) == OrderedDict(
+        [
+            ("cmd", "command"),
+            ("outs", [OrderedDict([("path", "input"), ("md5", "md-five")])]),
+        ]
+    )
+
+
+@pytest.mark.parametrize("typ", ["plots", "metrics", "outs"])
+def test_lock_outs_order(dvc, typ):
+    stage = create_stage(
+        PipelineStage, dvc, **{typ: ["input1", "input0"]}, **kwargs
+    )
+    stage.outs[0].info = {"md5": "md-one1"}
+    stage.outs[1].info = {"md5": "md-zer0"}
+    assert to_single_stage_lockfile(stage) == OrderedDict(
+        [
+            ("cmd", "command"),
+            (
+                "outs",
+                [
+                    OrderedDict([("path", "input0"), ("md5", "md-zer0")]),
+                    OrderedDict([("path", "input1"), ("md5", "md-one1")]),
+                ],
+            ),
+        ]
+    )
+
+
+def test_dump_appropriate_checksums(dvc):
+    stage = create_stage(
+        PipelineStage, dvc, deps=["s3://dvc-temp/file"], **kwargs
+    )
+    stage.deps[0].info = {"etag": "is-it-etag", "md5": "or-md5?"}
+    assert to_single_stage_lockfile(stage) == OrderedDict(
+        [
+            ("cmd", "command"),
+            (
+                "deps",
+                [
+                    OrderedDict(
+                        [
+                            ("path", "s3://dvc-temp/file"),
+                            ("etag", "is-it-etag"),
+                        ]
+                    )
+                ],
+            ),
+        ]
+    )
+
+
+def test_order(dvc):
+    stage = create_stage(
+        PipelineStage,
+        dvc,
+        deps=["input"],
+        outs=["output"],
+        params=["foo-param"],
+        **kwargs
+    )
+    params, deps = split_params_deps(stage)
+
+    deps[0].info = {"md5": "md-five"}
+    params[0].info = {"foo-param": "value"}
+    stage.outs[0].info = {"md5": "md5-output"}
+
+    assert to_single_stage_lockfile(stage) == OrderedDict(
+        [
+            ("cmd", "command"),
+            ("deps", [{"path": "input", "md5": "md-five"}]),
+            ("params", {"params.yaml": {"foo-param": "value"}}),
+            ("outs", [{"path": "output", "md5": "md5-output"}]),
+        ]
+    )
+
+
+def test_to_lockfile(dvc):
+    stage = create_stage(PipelineStage, dvc, deps=["input"], **kwargs)
+    stage.deps[0].info = {"md5": "md-five"}
+    entry = to_lockfile(stage)
+    assert len(entry) == 1
+    _Schema(LOCKFILE_SCHEMA)(entry)
+    assert entry == {
+        "something": OrderedDict(
+            [
+                ("cmd", "command"),
+                ("deps", [{"path": "input", "md5": "md-five"}]),
+            ]
+        )
+    }
diff --git a/tests/unit/stage/test_stage.py b/tests/unit/stage/test_stage.py
index 3da30f23a..7941322e9 100644
--- a/tests/unit/stage/test_stage.py
+++ b/tests/unit/stage/test_stage.py
@@ -2,7 +2,6 @@ import os
 import signal
 import subprocess
 import threading
-from unittest import TestCase
 
 import mock
 import pytest
@@ -50,12 +49,11 @@ def test_meta_ignored():
         assert stage.compute_md5() == "e9521a22111493406ea64a88cda63e0b"
 
 
-class TestPathConversion(TestCase):
-    def test(self):
-        stage = Stage(None, "path")
+def test_path_conversion(dvc):
+    stage = Stage(dvc, "path")
 
-        stage.wdir = os.path.join("..", "..")
-        self.assertEqual(stage.dumpd()["wdir"], "../..")
+    stage.wdir = os.path.join("..", "..")
+    assert stage.dumpd()["wdir"] == "../.."
 
 
 def test_stage_update(mocker):

pip install -e .[all]
parse_log_pytest
pytest
pytest pytest-cov pytest-xdist pytest-mock pytest-lazy-fixture flaky mock xmltodict awscli google-compute-engine Pygments collective.checkdocs flake8 psutil flake8-docstrings pydocstyle jaraco.windows mock-ssh-server moto rangehttpserver beautifulsoup4 flake8-bugbear flake8-comprehensions flake8-string-format pylint pylint-pytest pylint-plugin-utils wget filelock
apt-get update apt-get install -y gcc
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
aliyun-python-sdk-core==2.16.0
aliyun-python-sdk-core-v3==2.13.33
aliyun-python-sdk-kms==2.16.5
appdirs==1.4.4
astroid==2.15.8
atpublic==3.1.2
attrs @ file:///croot/attrs_1668696182826/work
autocommand==2.2.2
awscli==1.31.13
azure-common==1.1.28
azure-storage-blob==2.1.0
azure-storage-common==2.1.0
bcrypt==4.2.1
beautifulsoup4==4.13.3
boto==2.49.0
boto3==1.33.13
botocore==1.33.13
cachetools==4.2.4
certifi @ file:///croot/certifi_1671487769961/work/certifi
cffi==1.15.1
charset-normalizer==3.4.1
collective.checkdocs==0.2
colorama==0.4.4
configobj==5.0.9
coverage==7.2.7
crcmod==1.7
cryptography==44.0.2
decorator==5.1.1
dill==0.3.7
distro==1.9.0
docutils==0.16
dpath==2.2.0
-e git+https://github.com/iterative/dvc.git@c1e5b10cbd8d751c7036e26fe62f58edece0e507#egg=dvc
execnet==2.0.2
filelock==3.12.2
flake8==5.0.4
flake8-bugbear==23.3.12
flake8-comprehensions==3.13.0
flake8-docstrings==1.7.0
flake8-string-format==0.3.0
flaky==3.8.1
flatten-json==0.1.14
flit_core @ file:///opt/conda/conda-bld/flit-core_1644941570762/work/source/flit_core
flufl.lock==7.1.1
funcy==2.0
future==1.0.0
gitdb==4.0.12
GitPython==3.1.44
google-api-core==2.10.2
google-api-python-client==2.166.0
google-auth==1.35.0
google-auth-httplib2==0.2.0
google-cloud-core==1.7.3
google-cloud-storage==1.19.0
google-compute-engine==2.8.13
google-crc32c==1.5.0
google-resumable-media==2.7.2
googleapis-common-protos==1.69.2
grandalf==0.6
httplib2==0.22.0
idna==3.10
importlib-metadata==4.2.0
importlib-resources==5.12.0
inflect==6.0.5
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
isort==5.11.5
jaraco.classes==3.2.3
jaraco.collections==4.2.0
jaraco.context==4.3.0
jaraco.functools==3.7.0
jaraco.structures==2.1.0
jaraco.text==3.11.1
jaraco.ui==2.3.0
jaraco.windows==5.7.0
Jinja2==3.1.6
jmespath==0.10.0
jsonpath-ng==1.7.0
lazy-object-proxy==1.9.0
MarkupSafe==2.1.5
mccabe==0.7.0
mock==5.2.0
mock-ssh-server==0.9.1
more-itertools==9.1.0
moto==4.2.14
nanotime==0.5.2
networkx==2.3
numpy==1.21.6
oauth2client==4.1.3
oss2==2.6.1
packaging @ file:///croot/packaging_1671697413597/work
paramiko==3.5.1
path==16.6.0
pathspec==0.11.2
platformdirs==4.0.0
pluggy @ file:///tmp/build/80754af9/pluggy_1648042572264/work
ply==3.11
protobuf==4.24.4
psutil==7.0.0
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyarrow==12.0.1
pyasn1==0.5.1
pyasn1-modules==0.3.0
pycodestyle==2.9.1
pycparser==2.21
pycryptodome==3.22.0
pydantic==1.10.21
pydocstyle==6.3.0
pydot==2.0.0
PyDrive2==1.15.4
pyflakes==2.5.0
Pygments==2.17.2
pygtrie==2.3.2
pylint==2.17.7
pylint-plugin-utils==0.8.2
pylint-pytest==1.1.8
PyNaCl==1.5.0
pyOpenSSL==25.0.0
pyparsing==3.1.4
pytest==7.1.2
pytest-cov==4.1.0
pytest-lazy-fixture==0.6.3
pytest-mock==3.11.1
pytest-xdist==3.5.0
python-dateutil==2.9.0.post0
PyYAML==5.3.1
rangehttpserver==1.4.0
requests==2.31.0
responses==0.23.3
rsa==4.7.2
ruamel.yaml==0.18.10
ruamel.yaml.clib==0.2.8
s3transfer==0.8.2
shortuuid==1.0.13
six==1.17.0
smmap==5.0.2
snowballstemmer==2.2.0
soupsieve==2.4.1
tabulate==0.9.0
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
tomlkit==0.12.5
tqdm==4.67.1
typed-ast==1.5.5
types-PyYAML==6.0.12.12
typing_extensions==4.7.1
uritemplate==4.1.1
urllib3==1.26.20
voluptuous==0.14.1
Werkzeug==2.2.3
wget==3.2
wrapt==1.16.0
xmltodict==0.14.2
zc.lockfile==3.0.post1
zipp @ file:///croot/zipp_1672387121353/work

name: dvc
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=22.1.0=py37h06a4308_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - flit-core=3.6.0=pyhd3eb1b0_0
  - importlib_metadata=4.11.3=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=22.0=py37h06a4308_0
  - pip=22.3.1=py37h06a4308_0
  - pluggy=1.0.0=py37h06a4308_1
  - py=1.11.0=pyhd3eb1b0_0
  - pytest=7.1.2=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py37h06a4308_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.11.0=py37h06a4308_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - aliyun-python-sdk-core==2.16.0
      - aliyun-python-sdk-core-v3==2.13.33
      - aliyun-python-sdk-kms==2.16.5
      - appdirs==1.4.4
      - astroid==2.15.8
      - atpublic==3.1.2
      - autocommand==2.2.2
      - awscli==1.31.13
      - azure-common==1.1.28
      - azure-storage-blob==2.1.0
      - azure-storage-common==2.1.0
      - bcrypt==4.2.1
      - beautifulsoup4==4.13.3
      - boto==2.49.0
      - boto3==1.33.13
      - botocore==1.33.13
      - cachetools==4.2.4
      - cffi==1.15.1
      - charset-normalizer==3.4.1
      - collective-checkdocs==0.2
      - colorama==0.4.4
      - configobj==5.0.9
      - coverage==7.2.7
      - crcmod==1.7
      - cryptography==44.0.2
      - decorator==5.1.1
      - dill==0.3.7
      - distro==1.9.0
      - docutils==0.16
      - dpath==2.2.0
      - dvc==1.0.0a10+c1e5b1
      - execnet==2.0.2
      - filelock==3.12.2
      - flake8==5.0.4
      - flake8-bugbear==23.3.12
      - flake8-comprehensions==3.13.0
      - flake8-docstrings==1.7.0
      - flake8-string-format==0.3.0
      - flaky==3.8.1
      - flatten-json==0.1.14
      - flufl-lock==7.1.1
      - funcy==2.0
      - future==1.0.0
      - gitdb==4.0.12
      - gitpython==3.1.44
      - google-api-core==2.10.2
      - google-api-python-client==2.166.0
      - google-auth==1.35.0
      - google-auth-httplib2==0.2.0
      - google-cloud-core==1.7.3
      - google-cloud-storage==1.19.0
      - google-compute-engine==2.8.13
      - google-crc32c==1.5.0
      - google-resumable-media==2.7.2
      - googleapis-common-protos==1.69.2
      - grandalf==0.6
      - httplib2==0.22.0
      - idna==3.10
      - importlib-metadata==4.2.0
      - importlib-resources==5.12.0
      - inflect==6.0.5
      - isort==5.11.5
      - jaraco-classes==3.2.3
      - jaraco-collections==4.2.0
      - jaraco-context==4.3.0
      - jaraco-functools==3.7.0
      - jaraco-structures==2.1.0
      - jaraco-text==3.11.1
      - jaraco-ui==2.3.0
      - jaraco-windows==5.7.0
      - jinja2==3.1.6
      - jmespath==0.10.0
      - jsonpath-ng==1.7.0
      - lazy-object-proxy==1.9.0
      - markupsafe==2.1.5
      - mccabe==0.7.0
      - mock==5.2.0
      - mock-ssh-server==0.9.1
      - more-itertools==9.1.0
      - moto==4.2.14
      - nanotime==0.5.2
      - networkx==2.3
      - numpy==1.21.6
      - oauth2client==4.1.3
      - oss2==2.6.1
      - paramiko==3.5.1
      - path==16.6.0
      - pathspec==0.11.2
      - platformdirs==4.0.0
      - ply==3.11
      - protobuf==4.24.4
      - psutil==7.0.0
      - pyarrow==12.0.1
      - pyasn1==0.5.1
      - pyasn1-modules==0.3.0
      - pycodestyle==2.9.1
      - pycparser==2.21
      - pycryptodome==3.22.0
      - pydantic==1.10.21
      - pydocstyle==6.3.0
      - pydot==2.0.0
      - pydrive2==1.15.4
      - pyflakes==2.5.0
      - pygments==2.17.2
      - pygtrie==2.3.2
      - pylint==2.17.7
      - pylint-plugin-utils==0.8.2
      - pylint-pytest==1.1.8
      - pynacl==1.5.0
      - pyopenssl==25.0.0
      - pyparsing==3.1.4
      - pytest-cov==4.1.0
      - pytest-lazy-fixture==0.6.3
      - pytest-mock==3.11.1
      - pytest-xdist==3.5.0
      - python-dateutil==2.9.0.post0
      - pyyaml==5.3.1
      - rangehttpserver==1.4.0
      - requests==2.31.0
      - responses==0.23.3
      - rsa==4.7.2
      - ruamel-yaml==0.18.10
      - ruamel-yaml-clib==0.2.8
      - s3transfer==0.8.2
      - shortuuid==1.0.13
      - six==1.17.0
      - smmap==5.0.2
      - snowballstemmer==2.2.0
      - soupsieve==2.4.1
      - tabulate==0.9.0
      - tomlkit==0.12.5
      - tqdm==4.67.1
      - typed-ast==1.5.5
      - types-pyyaml==6.0.12.12
      - typing-extensions==4.7.1
      - uritemplate==4.1.1
      - urllib3==1.26.20
      - voluptuous==0.14.1
      - werkzeug==2.2.3
      - wget==3.2
      - wrapt==1.16.0
      - xmltodict==0.14.2
      - zc-lockfile==3.0.post1
prefix: /opt/conda/envs/dvc

tests/func/plots/test_plots.py::test_plot_csv_one_column tests/func/plots/test_plots.py::test_plot_csv_multiple_columns tests/func/plots/test_plots.py::test_plot_csv_choose_axes tests/func/plots/test_plots.py::test_plot_json_single_val tests/func/plots/test_plots.py::test_plot_json_multiple_val tests/func/plots/test_plots.py::test_plot_confusion tests/func/plots/test_plots.py::test_plot_multiple_revs_default tests/func/plots/test_plots.py::test_plot_multiple_revs tests/func/plots/test_plots.py::test_plot_even_if_metric_missing tests/func/plots/test_plots.py::test_throw_on_no_metric_at_all tests/func/plots/test_plots.py::test_custom_template tests/func/plots/test_plots.py::test_custom_template_with_specified_data tests/func/plots/test_plots.py::test_plot_override_specified_data_source tests/func/plots/test_plots.py::test_no_plots tests/func/plots/test_plots.py::test_should_raise_on_no_template tests/func/plots/test_plots.py::test_plot_no_data tests/func/plots/test_plots.py::test_plot_wrong_metric_type tests/func/plots/test_plots.py::test_plot_choose_columns tests/func/plots/test_plots.py::test_plot_default_choose_column tests/func/plots/test_plots.py::test_plot_yaml tests/func/plots/test_plots.py::test_raise_on_wrong_field tests/func/plots/test_plots.py::test_load_metric_from_dict_json tests/func/plots/test_plots.py::test_load_metric_from_dict_yaml tests/func/test_lockfile.py::test_deps_outs_are_sorted_by_path tests/func/test_lockfile.py::test_order_is_preserved_when_pipeline_order_changes tests/func/test_lockfile.py::test_cmd_changes_other_orders_are_preserved tests/func/test_lockfile.py::test_params_dump tests/func/test_repro_multistage.py::TestReproFailMultiStage::test tests/func/test_repro_multistage.py::TestReproCyclicGraphMultiStage::test tests/func/test_repro_multistage.py::TestReproUnderDirMultiStage::test tests/func/test_repro_multistage.py::TestReproDepDirWithOutputsUnderItMultiStage::test tests/func/test_repro_multistage.py::TestReproNoDepsMultiStage::test tests/func/test_repro_multistage.py::TestReproForceMultiStage::test tests/func/test_repro_multistage.py::TestReproChangedCodeMultiStage::test tests/func/test_repro_multistage.py::TestReproChangedDataMultiStage::test tests/func/test_repro_multistage.py::TestReproDry::test tests/func/test_repro_multistage.py::TestReproUpToDateMultiStage::test tests/func/test_repro_multistage.py::TestReproChangedDeepDataMultiStage::test tests/func/test_repro_multistage.py::TestReproPipelineMultiStage::test tests/func/test_repro_multistage.py::TestReproPipelineMultiStage::test_cli tests/func/test_repro_multistage.py::TestReproPipelinesMultiStage::test tests/func/test_repro_multistage.py::TestReproPipelinesMultiStage::test_cli tests/func/test_repro_multistage.py::TestReproFrozenMultiStage::test tests/func/test_repro_multistage.py::TestReproFrozenMultiStage::test_non_existing tests/func/test_repro_multistage.py::TestReproFrozenCallbackMultiStage::test tests/func/test_repro_multistage.py::TestReproFrozenUnchangedMultiStage::test tests/func/test_repro_multistage.py::TestReproPhonyMultiStage::test tests/func/test_repro_multistage.py::TestCmdReproMultiStage::test tests/func/test_repro_multistage.py::TestReproAllPipelinesMultiStage::test tests/func/test_repro_multistage.py::TestReproNoCommit::test tests/func/test_repro_multistage.py::TestNonExistingOutputMultiStage::test tests/func/test_repro_multistage.py::TestReproAlreadyCachedMultiStage::test tests/func/test_repro_multistage.py::TestReproAlreadyCachedMultiStage::test_force_import tests/func/test_repro_multistage.py::TestReproAlreadyCachedMultiStage::test_force_with_dependencies tests/func/test_repro_multistage.py::TestReproChangedDirMultiStage::test tests/func/test_repro_multistage.py::TestReproChangedDirDataMultiStage::test tests/func/test_repro_multistage.py::TestReproExternalHTTPMultiStage::test tests/func/test_repro_multistage.py::TestReproExternalLOCALMultiStage::test tests/func/test_repro_multistage.py::test_non_existing_stage_name tests/func/test_repro_multistage.py::test_downstream tests/func/test_repro_multistage.py::test_repro_when_cmd_changes tests/func/test_repro_multistage.py::test_repro_when_new_deps_is_added_in_dvcfile tests/func/test_repro_multistage.py::test_repro_when_new_outs_is_added_in_dvcfile tests/func/test_repro_multistage.py::test_repro_when_new_deps_is_moved tests/func/test_repro_multistage.py::test_repro_when_new_out_overlaps_others_stage_outs tests/func/test_repro_multistage.py::test_repro_when_new_deps_added_does_not_exist tests/func/test_repro_multistage.py::test_repro_when_new_outs_added_does_not_exist tests/func/test_repro_multistage.py::test_repro_when_lockfile_gets_deleted tests/func/test_repro_multistage.py::test_cyclic_graph_error tests/func/test_repro_multistage.py::test_repro_multiple_params tests/unit/stage/test_loader_pipeline_file.py::test_fill_from_lock_deps_outs tests/unit/stage/test_loader_pipeline_file.py::test_fill_from_lock_params tests/unit/stage/test_loader_pipeline_file.py::test_fill_from_lock_missing_params_section tests/unit/stage/test_loader_pipeline_file.py::test_fill_from_lock_missing_checksums tests/unit/stage/test_loader_pipeline_file.py::test_fill_from_lock_use_appropriate_checksum tests/unit/stage/test_loader_pipeline_file.py::test_fill_from_lock_with_missing_sections tests/unit/stage/test_loader_pipeline_file.py::test_fill_from_lock_empty_data tests/unit/stage/test_loader_pipeline_file.py::test_load_stage tests/unit/stage/test_loader_pipeline_file.py::test_load_stage_outs_with_flags tests/unit/stage/test_loader_pipeline_file.py::test_load_stage_no_lock tests/unit/stage/test_loader_pipeline_file.py::test_load_stage_with_params tests/unit/stage/test_loader_pipeline_file.py::test_load_stage_with_metrics_and_plots[metrics] tests/unit/stage/test_loader_pipeline_file.py::test_load_stage_with_metrics_and_plots[plots] tests/unit/stage/test_loader_pipeline_file.py::test_load_changed_command tests/unit/stage/test_loader_pipeline_file.py::test_load_stage_wdir_and_path_correctly tests/unit/stage/test_loader_pipeline_file.py::test_load_stage_mapping tests/unit/stage/test_serialize_pipeline_file.py::test_cmd tests/unit/stage/test_serialize_pipeline_file.py::test_wdir tests/unit/stage/test_serialize_pipeline_file.py::test_deps_sorted tests/unit/stage/test_serialize_pipeline_file.py::test_outs_sorted tests/unit/stage/test_serialize_pipeline_file.py::test_params_sorted tests/unit/stage/test_serialize_pipeline_file.py::test_params_file_sorted tests/unit/stage/test_serialize_pipeline_file.py::test_outs_and_outs_flags_are_sorted[plots-extra0] tests/unit/stage/test_serialize_pipeline_file.py::test_outs_and_outs_flags_are_sorted[metrics-extra1] tests/unit/stage/test_serialize_pipeline_file.py::test_outs_and_outs_flags_are_sorted[outs-extra2] tests/unit/stage/test_serialize_pipeline_file.py::test_plot_props tests/unit/stage/test_serialize_pipeline_file.py::test_frozen tests/unit/stage/test_serialize_pipeline_file.py::test_always_changed tests/unit/stage/test_serialize_pipeline_file.py::test_order tests/unit/stage/test_serialize_pipeline_file.py::test_order_deps_outs[outs] tests/unit/stage/test_serialize_pipeline_file.py::test_order_deps_outs[metrics] tests/unit/stage/test_serialize_pipeline_file.py::test_order_deps_outs[plots] tests/unit/stage/test_serialize_pipeline_file.py::test_order_deps_outs[params] tests/unit/stage/test_serialize_pipeline_file.py::test_order_deps_outs[deps] tests/unit/stage/test_serialize_pipeline_file.py::test_order_deps_outs[None] tests/unit/stage/test_serialize_pipeline_lock.py::test_lock tests/unit/stage/test_serialize_pipeline_lock.py::test_lock_deps tests/unit/stage/test_serialize_pipeline_lock.py::test_lock_deps_order tests/unit/stage/test_serialize_pipeline_lock.py::test_lock_params tests/unit/stage/test_serialize_pipeline_lock.py::test_lock_params_file_sorted tests/unit/stage/test_serialize_pipeline_lock.py::test_lock_params_no_values_filled tests/unit/stage/test_serialize_pipeline_lock.py::test_lock_outs[plots] tests/unit/stage/test_serialize_pipeline_lock.py::test_lock_outs[metrics] tests/unit/stage/test_serialize_pipeline_lock.py::test_lock_outs[outs] tests/unit/stage/test_serialize_pipeline_lock.py::test_lock_outs_order[plots] tests/unit/stage/test_serialize_pipeline_lock.py::test_lock_outs_order[metrics] tests/unit/stage/test_serialize_pipeline_lock.py::test_lock_outs_order[outs] tests/unit/stage/test_serialize_pipeline_lock.py::test_dump_appropriate_checksums tests/unit/stage/test_serialize_pipeline_lock.py::test_order tests/unit/stage/test_serialize_pipeline_lock.py::test_to_lockfile tests/unit/stage/test_stage.py::test_stage_checksum tests/unit/stage/test_stage.py::test_wdir_default_ignored tests/unit/stage/test_stage.py::test_wdir_non_default_is_not_ignored tests/unit/stage/test_stage.py::test_meta_ignored tests/unit/stage/test_stage.py::test_path_conversion tests/unit/stage/test_stage.py::test_stage_update tests/unit/stage/test_stage.py::test_stage_run_ignore_sigint tests/unit/stage/test_stage.py::test_always_changed tests/unit/stage/test_stage.py::test_external_outs
dvc/serialize.py dvc/stage/cache.py dvc/stage/utils.py dvc/stage/imports.py dvc/repo/plots/data.py dvc/stage/run.py dvc/stage/__init__.py setup.cfg dvc/dependency/__init__.py dvc/dvcfile.py dvc/command/plots.py
dvc/stage/cache.py dvc/stage/utils.py dvc/stage/imports.py dvc/repo/plots/data.py dvc/stage/run.py dvc/stage/__init__.py dvc/stage/serialize.py setup.cfg dvc/dependency/__init__.py dvc/dvcfile.py dvc/command/plots.py