Test cases in Grep exercise

Hello world, I have a few problems with the Grep exercise in the Elixir track.

Print only file names

The task description for the -l flag reads:

-l Output only the names of the files that contain at least one matching line.

The “real” grep -l prints the name of a file containing a match only once.
There is no test case for this like the following:

    # test for "real grep" behaviour when printing file names
    test "several matches, print file names flag" do
      assert Grep.grep("may", ["-l"], ["midsummer-night.txt"]) ==
               """
               midsummer-night.txt
               """
    end

Multiple flags

Also, I managed to do flag handling wrong in exactly the right way so that this test passes

    test "one match, multiple flags" do
      assert Grep.grep("OF ATREUS, Agamemnon, KIng of MEN.", ["-n", "-i", "-x"], ["iliad.txt"]) ==
               """
               9:Of Atreus, Agamemnon, King of men.
               """
    end

although my implementation ignores -x after finding -i.

filter =
      cond do
        "-i" in flags ->
          fn {line, _} -> String.contains?(String.downcase(line), String.downcase(pattern)) end

        "-x" in flags ->
          fn {line, _} -> "#{pattern}\n" == line end

        true ->
          fn {line, _} -> String.contains?(line, pattern) end
      end

There should be another test that makes sure that -x is actually respected:

    test "one match, -i -x flags" do
      assert Grep.grep("OF ATREUS", ["-i", "-x"], ["iliad.txt"]) ==
               """
               """
    end

Let me know what you think.

Regards,
Stefan

You might find this worth reading.

Hi, Elixir maintainer here :wave: A quick TL;DR: of the link IsaacG posted: tests for practice exercises are global to the whole Exercism organization and all programming languages share it. This is the test definition for the grep exercise.

Your suggestions apply to the global test data and aren’t Elixir-specific, so maybe a moderator could move this thread out of the Elixir category?

As a personal rule, I try to stay out of discussions about what should be tested and what should not because that’s not where my expertise lies. Since this is not Elixir-specific, I won’t help here. But somebody else might.

I see this test from canonical data to do the “Print only file names” testing (multiple matches in multiple files, implies returning the file names once for paradise-lost.txt).

The second suggested test case seems new. As this exercise is all about flags and their combinations, I’d like to see this added.

Doesn’t this test case cover the -x case?

It covers -x when used without -i.

There is a test one match, multiple flags targeting this in combination with -n. But since the pattern there is the whole line, it only verifies that a complete line is found. It does not verify that a partial match is not found.

Are you saying that we need to test for a partial match both with and without -i? Why would -i impact whether or not partial matching happens?

I am saying that I managed to create a defective piece of code that passed the current test suite. We agree that in a correct implementation -i does not influence partial/full matching.

But since in the current test the whole line used as pattern is also a partial match, the current test alone cannot decide if the -x is respected if -i is present.

The code quoted in my first message uses partial matching when -i is detected and ignores -x and passes the test suite. The additional test catches this mistake.

I took a look at the community solutions on the elixir track and found something similar in the first one (sort by oldest) I looked at: Iteration 1 passed with the same faulty flag handling, mentored iteration 2 contains a fix. (I wonder how a solution submitted 3 years ago is in front of solutions from 6 years ago, but I will not get distracted now :-)

After many solutions without this issue, I found another one.

Line 28ff:

  defp filter(pattern, flags) do
    fn {_, _, line} ->
      cond do
        "-v" in flags -> not String.contains?(line, pattern)
        "-i" in flags -> String.contains?(String.upcase(line), String.upcase(pattern))
        "-x" in flags -> line == pattern <> "\n"
        :else -> String.contains?(line, pattern)
      end
    end
  end

-v and -i both use partial matches and get away with it.

So there might be a few more tests missing, as -v -x should report all lines that are not a full match, while here -v -x is treated the same as -v and reports all lines that do not have a partial match.