Fork me on GitHub
#clojure
<
2021-08-15
>
dominicm13:08:37

Does anyone have recommendations for testing with sets but producing an order? e.g. (string/join "," #{"a" "b"}) and you want to test that the result is either "a,b" or "b,a"? (but obviously the combinations get bigger if your set is 3, 4, etc.)

p-himik13:08:52

So, for a set of size N it would test for all N! permutations? Can you give an example for a larger N where you define the expected results? I imagine it won't be hard-coding values like "a,b" but instead would be something generative. And if so, it might either become an obvious case of testing 1 = 1 or provide some additional useful information.

dominicm13:08:35

I'm not interested in generative testing here. I mean only that for:

(is (= "a,b" (string/join "," #{"a" "b"})))
Is an invalid test. It should instead be:
(is (#{"a,b" "b,a"} (string/join ...)))
But for #{"a", "b", "c"}, etc. it gets annoying.

p-himik13:08:37

By "generative" I meant that you would write some code to generate "a,b" and not that you would generate random data.

p-himik13:08:43

In the case of string/join it 100% feels like testing 1 = 1 , because I imagine such "a,b" value generation would use string/join itself.

dominicm13:08:44

Ah, I see. I'm quite tired, apologies. I suppose I could implement a version of the tested function which could combine with combinatorics to generate ordered results. That might not be a useful function for testing though.

dominicm13:08:23

Maybe I could use a sorted-set for testing? That makes sense.

dominicm13:08:04

hum, except my values aren't comparable #{:none "ws:"} 🙂

p-himik13:08:45

I don't know a thing about what code you currently have, so it's just a random guess, but perhaps that functionality that you want to test could be split into two - one that deals with something ordered, like vectors and lists, and the other that processes sets, turns them into something ordered that's intended to be fed into that first part? That way, you could test these two things separately - it should make testing much simpler.

dominicm13:08:08

The idea was to have a consistent data-structure for performing operations on. I'm representing CSPs as data. One of the operations is merging, which requires sets. High performance is a goal though, so switching between data types isn't something I'm keen on.

emccue13:08:48

just sort the set you get back

emccue13:08:09

1st assert it is a set

emccue13:08:34

then assert the contents are correct via =

emccue13:08:23

then sort before passing it to a function OR reverse the rsult of that function back into a set (like splitting on commas in a string/join(

dominicm13:08:29

@U3JH98J4R it's set->string not set->set.

borkdude13:08:18

parse the string, then sort?

mpenet14:08:51

Or use (into (sorted-set) s) on input

mpenet14:08:36

Oh... just read about comparable issue with your values

emccue14:08:57

(sorted-set-by #(compare (str %1) (str %2)))

p-himik14:08:10

I would try to improve that comparator though:

user=> (sorted-set-by #(compare (str %1) (str %2)) 1 "1")
#{1}

emccue14:08:46

depends on their input data - but there should be something that can make it work

p-himik14:08:58

Data which we know nothing about. ;) My intention was to only point out that the comparator is not to be used as is blindly.

emccue14:08:23

well their example was #{:none "ws:"} - thats what i was going off of - still technically nondeterministic for strings starting with :, but 🤷

quoll14:08:02

In the past I’ve liked to compare by type, then by value:

(def cmp
  (reify java.util.Comparator
    (compare [_ a b]
      (if (= (class a) (class b))
        (compare a b)
        (compare (str (class a)) (str (class b)))))))
Then I use sort-by on the set, though a sorted-set-by can also be used:
(is (= "a,b" (string/join "," (sort-by identity cmp #{"a" "b"}))))
(it’s a little annoying to start with (sort-by identity...), but than can be turned into a function easily)

quoll14:08:13

Asami actually needs to sort like this, since it has a tree structure (on disk) that can contain various types, and so I have to sort by type first. (supported types have a bit-pattern, and so I sort on that, and not by type names)

dominicm20:08:35

heh, I like the idea of a custom comparator.

quoll23:08:25

They’re a little verbose (as shown), but flexible, and you only need to define it the once.

pinkfrog16:08:19

Hi. How can I refer to an ns without loading it

Alex Miller (Clojure team)16:08:26

in what context are you referring to it?

pinkfrog16:08:40

It’s like defining an alias for a namespace but without importing it.

pinkfrog16:08:41

I will try the alias function first.

emccue16:08:03

I don't know if something like that exists...BUT

emccue16:08:16

(defn to-namespaced [ns un-namespaced-thing]
  (if (symbol? un-namespaced-thing)
    (symbol ns (name un-namespaced-thing))
    (keyword ns (name un-namespaced-thing)))))

(def foo (partial to-namespaced "some.path.to.bar.foo"))

(foo :abc) ; => :some.path.to.bar.foo/abc

emccue16:08:51

and then convert to a macro to pay no runtime cost beyond startup

Alex Miller (Clojure team)17:08:03

it really depends a lot on what you're actually trying to do

Alex Miller (Clojure team)17:08:07

if you are getting hung up on aliasing namespaced keywords for namespaces that don't exist, the most common approach right now is to use create-ns and alias to construct a non-loaded runtime namespace

Alex Miller (Clojure team)17:08:42

this is actually exactly what I'm working on for Clojure 1.11 right now so there will be another option (at some point)

🎉 3