protected Map<Path, Path> compile(final Move move, final ListService list, final Path source, final Path target) throws BackgroundException {
        // Compile recursive list
        final Map<Path, Path> recursive = new LinkedHashMap<>();
        recursive.put(source, target);
        if(source.isDirectory()) {
            if(!move.isRecursive(source, target)) {
                // sort ascending by timestamp to move older versions first
                final AttributedList<Path> children = list.list(source, new WorkerListProgressListener(this, listener))
                        .filter(new VersionsComparator(true));
                for(Path child : children) {
                    if(this.isCanceled()) {
                        throw new ConnectionCanceledException();
                    }
                    recursive.putAll(this.compile(move, list, child, new Path(target, child.getName(), child.getType())));
                }
            }
        }
        return recursive;
    }
@Test
    public void testCompile() throws Exception {
        final Session session = new NullSession(new Host(new TestProtocol())) {
            @Override
            @SuppressWarnings("unchecked")
            public <T> T _getFeature(final Class<T> type) {
                if(type == Delete.class) {
                    return (T) new Delete() {
                        private final AtomicInteger count = new AtomicInteger();

                        @Override
                        public void delete(final Map<Path, TransferStatus> files, final PasswordCallback prompt, final Callback callback) {
                            for(Path file : files.keySet()) {
                                if(count.get() == 0) {
                                    assertEquals(new Path("/t/d", EnumSet.of(Path.Type.directory)), file);
                                }
                                if(count.get() == 1) {
                                    assertEquals(new Path("/t", EnumSet.of(Path.Type.directory)), file);
                                }
                                count.incrementAndGet();
                            }
                        }

                        @Override
                        public boolean isRecursive() {
                            return false;
                        }
                    };
                }
                if(type == Directory.class) {
                    return (T) new Directory<Void>() {

                        @Override
                        public Path mkdir(final Path folder, final TransferStatus status) {
                            return folder;
                        }

                        @Override
                        public Directory<Void> withWriter(final Write<Void> writer) {
                            return this;
                        }
                    };
                }
                if(type == Move.class) {
                    return (T) new Move() {
                        private final AtomicInteger count = new AtomicInteger();
                        private final PathAttributes attr = new PathAttributes().withSize(1L);

                        @Override
                        public Path move(final Path file, final Path renamed, final TransferStatus status, final Delete.Callback callback, final ConnectionCallback connectionCallback) {
                            if(count.get() == 0) {
                                assertEquals(new Path("/t/a", EnumSet.of(Path.Type.file)), file);
                                assertEquals(new Path("/t2/a", EnumSet.of(Path.Type.file)), renamed);
                            }
                            if(count.get() == 1) {
                                assertEquals(new Path("/t/d/b", EnumSet.of(Path.Type.file)), file);
                                assertEquals(new Path("/t2/d/b", EnumSet.of(Path.Type.file)), renamed);
                            }
                            if(count.get() == 2) {
                                assertEquals(new Path("/t/d", EnumSet.of(Path.Type.directory)), file);
                                assertEquals(new Path("/t2/d", EnumSet.of(Path.Type.directory)), renamed);
                            }
                            if(count.get() == 3) {
                                assertEquals(new Path("/t", EnumSet.of(Path.Type.directory)), file);
                                assertEquals(new Path("/t2", EnumSet.of(Path.Type.directory)), renamed);
                            }
                            count.incrementAndGet();
                            return renamed.withAttributes(attr);
                        }

                        @Override
                        public boolean isRecursive(final Path source, final Path target) {
                            return false;
                        }

                    };
                }
                return super._getFeature(type);
            }

            @Override
            public AttributedList<Path> list(final Path file, final ListProgressListener listener) {
                if(file.equals(new Path("/t", EnumSet.of(Path.Type.directory)))) {
                    return new AttributedList<>(Arrays.asList(
                            new Path("/t/a", EnumSet.of(Path.Type.file)),
                            new Path("/t/d", EnumSet.of(Path.Type.directory))
                    ));
                }
                if(file.equals(new Path("/t/d", EnumSet.of(Path.Type.directory)))) {
                    return new AttributedList<>(Collections.singletonList(
                            new Path("/t/d/b", EnumSet.of(Path.Type.file))
                    ));
                }
                if(file.equals(new Path("/", EnumSet.of(Path.Type.directory)))) {
                    return AttributedList.emptyList();
                }
                if(file.equals(new Path("/t2", EnumSet.of(Path.Type.directory)))) {
                    return AttributedList.emptyList();
                }
                if(file.equals(new Path("/t2/d", EnumSet.of(Path.Type.directory)))) {
                    return AttributedList.emptyList();
                }
                fail();
                return null;
            }
        };
        final MoveWorker worker = new MoveWorker(
            Collections.singletonMap(new Path("/t", EnumSet.of(Path.Type.directory)), new Path("/t2", EnumSet.of(Path.Type.directory))),
            new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledLoginCallback());
        final Collection<Path> targets = worker.run(session).values();
        assertEquals(4, targets.size());
        assertTrue(targets.contains(new Path("/t2", EnumSet.of(Path.Type.directory))));
        assertTrue(targets.contains(new Path("/t2/a", EnumSet.of(Path.Type.file))));
        assertTrue(targets.contains(new Path("/t2/d", EnumSet.of(Path.Type.directory))));
        assertTrue(targets.contains(new Path("/t2/d/b", EnumSet.of(Path.Type.file))));
    }