Package co.paralleluniverse.fibers

Source Code of co.paralleluniverse.fibers.FiberTest$SerFiber

/*
* Quasar: lightweight threads and actors for the JVM.
* Copyright (c) 2013-2014, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*   or (per the licensee's choosing)
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.fibers;

import co.paralleluniverse.common.util.Exceptions;
import co.paralleluniverse.io.serialization.ByteArraySerializer;
import co.paralleluniverse.strands.Condition;
import co.paralleluniverse.strands.SettableFuture;
import co.paralleluniverse.strands.SimpleConditionSynchronizer;
import co.paralleluniverse.strands.Strand;
import co.paralleluniverse.strands.SuspendableCallable;
import co.paralleluniverse.strands.SuspendableRunnable;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.Serializable;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.*;

import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

/**
*
* @author pron
*/
//@RunWith(Parameterized.class)
public class FiberTest implements Serializable {
    private transient FiberScheduler scheduler;

    public FiberTest() {
//        this.scheduler = new FiberExecutorScheduler("test", Executors.newFixedThreadPool(1));
        this.scheduler = new FiberForkJoinScheduler("test", 4, null, false);
    }
//    public FiberTest(FiberScheduler scheduler) {
//        this.scheduler = scheduler;
//    }

    @Parameterized.Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][]{
            {new FiberForkJoinScheduler("test", 4, null, false)},
            {new FiberExecutorScheduler("test", Executors.newFixedThreadPool(1, new ThreadFactoryBuilder().setNameFormat("fiber-scheduler-%d").setDaemon(true).build()))},});
    }

    @BeforeClass
    public static void setUpClass() {
        Fiber.setDefaultUncaughtExceptionHandler(new Strand.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Strand s, Throwable e) {
                Exceptions.rethrow(e);
            }
        });
    }

    @Before
    public void before() {
//        if (scheduler instanceof FiberForkJoinScheduler)
//            System.out.println("==> " + ((FiberForkJoinScheduler) scheduler).getForkJoinPool().getClass().getSuperclass().getName());
    }

    @Test
    public void testTimeout() throws Exception {
        Fiber fiber = new Fiber(scheduler, new SuspendableRunnable() {
            @Override
            public void run() throws SuspendExecution {
                Fiber.park(100, TimeUnit.MILLISECONDS);
            }
        }).start();

        try {
            fiber.join(50, TimeUnit.MILLISECONDS);
            fail();
        } catch (java.util.concurrent.TimeoutException e) {
        }

        fiber.join(200, TimeUnit.MILLISECONDS);
    }

    @Test
    public void testJoinFromFiber() throws Exception {
        final Fiber<Integer> fiber1 = new Fiber<Integer>(scheduler, new SuspendableCallable<Integer>() {
            @Override
            public Integer run() throws SuspendExecution {
                Fiber.park(100, TimeUnit.MILLISECONDS);
                return 123;
            }
        }).start();

        final Fiber<Integer> fiber2 = new Fiber<Integer>(scheduler, new SuspendableCallable<Integer>() {
            @Override
            public Integer run() throws SuspendExecution, InterruptedException {
                try {
                    int res = fiber1.get();
                    return res;
                } catch (ExecutionException e) {
                    throw Exceptions.rethrow(e.getCause());
                }
            }
        }).start();

        int res = fiber2.get();

        assertThat(res, is(123));
        assertThat(fiber1.get(), is(123));
    }

    @Test
    public void testInterrupt() throws Exception {
        Fiber fiber = new Fiber(scheduler, new SuspendableRunnable() {
            @Override
            public void run() throws SuspendExecution {
                try {
                    Fiber.sleep(100);
                    fail("InterruptedException not thrown");
                } catch (InterruptedException e) {
                }
            }
        }).start();

        Thread.sleep(20);
        fiber.interrupt();
        fiber.join(5, TimeUnit.MILLISECONDS);
    }

    @Test
    public void testThreadLocals() throws Exception {
        final ThreadLocal<String> tl1 = new ThreadLocal<>();
        final InheritableThreadLocal<String> tl2 = new InheritableThreadLocal<>();
        tl1.set("foo");
        tl2.set("bar");

        Fiber fiber = new Fiber(scheduler, new SuspendableRunnable() {
            @Override
            public void run() throws SuspendExecution, InterruptedException {
                assertThat(tl1.get(), is(nullValue()));
                assertThat(tl2.get(), is("bar"));

                tl1.set("koko");
                tl2.set("bubu");

                assertThat(tl1.get(), is("koko"));
                assertThat(tl2.get(), is("bubu"));

                Fiber.sleep(100);

                assertThat(tl1.get(), is("koko"));
                assertThat(tl2.get(), is("bubu"));
            }
        });
        fiber.start();
        fiber.join();

        assertThat(tl1.get(), is("foo"));
        assertThat(tl2.get(), is("bar"));
    }

    @Test
    public void testInheritThreadLocals() throws Exception {
        final ThreadLocal<String> tl1 = new ThreadLocal<>();
        tl1.set("foo");

        Fiber fiber = new Fiber(scheduler, new SuspendableRunnable() {
            @Override
            public void run() throws SuspendExecution, InterruptedException {
                assertThat(tl1.get(), is("foo"));

                Fiber.sleep(100);

                assertThat(tl1.get(), is("foo"));

                tl1.set("koko");

                assertThat(tl1.get(), is("koko"));

                Fiber.sleep(100);

                assertThat(tl1.get(), is("koko"));
            }
        });
        fiber.inheritThreadLocals().start();
        fiber.join();

        assertThat(tl1.get(), is("foo"));
    }

    @Test
    public void testThreadLocalsParallel() throws Exception {
        final ThreadLocal<String> tl = new ThreadLocal<>();

        final int n = 100;
        final int loops = 100;
        Fiber[] fibers = new Fiber[n];
        for (int i = 0; i < n; i++) {
            final int id = i;
            Fiber fiber = new Fiber(scheduler, new SuspendableRunnable() {
                @Override
                public void run() throws SuspendExecution, InterruptedException {
                    for (int j = 0; j < loops; j++) {
                        final String tlValue = "tl-" + id + "-" + j;
                        tl.set(tlValue);
                        assertThat(tl.get(), equalTo(tlValue));
                        Strand.sleep(10);
                        assertThat(tl.get(), equalTo(tlValue));
                    }
                }
            });
            fiber.start();
            fibers[i] = fiber;
        }

        for (Fiber fiber : fibers)
            fiber.join();
    }

    @Test
    public void testInheritThreadLocalsParallel() throws Exception {
        final ThreadLocal<String> tl = new ThreadLocal<>();
        tl.set("foo");

        final int n = 100;
        final int loops = 100;
        Fiber[] fibers = new Fiber[n];
        for (int i = 0; i < n; i++) {
            final int id = i;
            Fiber fiber = new Fiber(scheduler, new SuspendableRunnable() {
                @Override
                public void run() throws SuspendExecution, InterruptedException {
                    for (int j = 0; j < loops; j++) {
                        final String tlValue = "tl-" + id + "-" + j;
                        tl.set(tlValue);
                        assertThat(tl.get(), equalTo(tlValue));
                        Strand.sleep(10);
                        assertThat(tl.get(), equalTo(tlValue));
                    }
                }
            }).inheritThreadLocals();
            fiber.start();
            fibers[i] = fiber;
        }

        for (Fiber fiber : fibers)
            fiber.join();
    }

    @Test
    public void whenFiberIsNewThenDumpStackReturnsNull() throws Exception {
        Fiber fiber = new Fiber(scheduler, new SuspendableRunnable() {
            @Override
            public void run() throws SuspendExecution, InterruptedException {
                foo();
            }

            private void foo() {
            }
        });

        StackTraceElement[] st = fiber.getStackTrace();
        assertThat(st, is(nullValue()));
    }

    @Test
    public void whenFiberIsTerminatedThenDumpStackReturnsNull() throws Exception {
        Fiber fiber = new Fiber(scheduler, new SuspendableRunnable() {
            @Override
            public void run() throws SuspendExecution, InterruptedException {
                foo();
            }

            private void foo() {
            }
        }).start();

        fiber.join();

        StackTraceElement[] st = fiber.getStackTrace();
        assertThat(st, is(nullValue()));
    }

    @Test
    public void testDumpStackCurrentFiber() throws Exception {
        Fiber fiber = new Fiber(scheduler, new SuspendableRunnable() {
            @Override
            public void run() throws SuspendExecution, InterruptedException {
                foo();
            }

            private void foo() {
                StackTraceElement[] st = Fiber.currentFiber().getStackTrace();

                // Strand.printStackTrace(st, System.err);
                assertThat(st[0].getMethodName(), equalTo("getStackTrace"));
                assertThat(st[1].getMethodName(), equalTo("foo"));
                assertThat(st[st.length - 1].getMethodName(), equalTo("run"));
                assertThat(st[st.length - 1].getClassName(), equalTo(Fiber.class.getName()));
            }
        }).start();

        fiber.join();
    }

    @Test
    public void testDumpStackRunningFiber() throws Exception {
        Fiber fiber = new Fiber(scheduler, new SuspendableRunnable() {
            @Override
            public void run() throws SuspendExecution, InterruptedException {
                foo();
            }

            private void foo() {
                final long start = System.nanoTime();
                for (;;) {
                    if (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start) > 1000)
                        break;
                }
            }
        }).start();

        Thread.sleep(200);

        StackTraceElement[] st = fiber.getStackTrace();

        // Strand.printStackTrace(st, System.err);
        boolean found = false;
        for (int i = 0; i < st.length; i++) {
            if (st[i].getMethodName().equals("foo")) {
                found = true;
                break;
            }
        }
        assertThat(found, is(true));
        assertThat(st[st.length - 1].getMethodName(), equalTo("run"));
        assertThat(st[st.length - 1].getClassName(), equalTo(Fiber.class.getName()));

        fiber.join();
    }

    @Test
    public void testDumpStackWaitingFiber() throws Exception {
        final Condition cond = new SimpleConditionSynchronizer(null);
        final AtomicBoolean flag = new AtomicBoolean(false);

        Fiber fiber = new Fiber(scheduler, new SuspendableRunnable() {
            @Override
            public void run() throws SuspendExecution, InterruptedException {
                foo();
            }

            private void foo() throws InterruptedException, SuspendExecution {
                Object token = cond.register();
                try {
                    for (int i = 0; !flag.get(); i++)
                        cond.await(i);
                } finally {
                    cond.unregister(token);
                }
            }
        }).start();

        Thread.sleep(200);

        StackTraceElement[] st = fiber.getStackTrace();

        // Strand.printStackTrace(st, System.err);
        assertThat(st[0].getMethodName(), equalTo("park"));
        boolean found = false;
        for (StackTraceElement ste : st) {
            if (ste.getMethodName().equals("foo")) {
                found = true;
                break;
            }
        }
        assertThat(found, is(true));
        assertThat(st[st.length - 1].getMethodName(), equalTo("run"));
        assertThat(st[st.length - 1].getClassName(), equalTo(Fiber.class.getName()));

        flag.set(true);
        cond.signalAll();

        fiber.join();
    }

    @Test
    public void testDumpStackWaitingFiberWhenCalledFromFiber() throws Exception {
        final Condition cond = new SimpleConditionSynchronizer(null);
        final AtomicBoolean flag = new AtomicBoolean(false);

        final Fiber fiber = new Fiber(scheduler, new SuspendableRunnable() {
            @Override
            public void run() throws SuspendExecution, InterruptedException {
                foo();
            }

            private void foo() throws InterruptedException, SuspendExecution {
                Object token = cond.register();
                try {
                    for (int i = 0; !flag.get(); i++)
                        cond.await(i);
                } finally {
                    cond.unregister(token);
                }
            }
        }).start();

        Thread.sleep(200);

        Fiber fiber2 = new Fiber(scheduler, new SuspendableRunnable() {
            @Override
            public void run() throws SuspendExecution, InterruptedException {
                StackTraceElement[] st = fiber.getStackTrace();

                // Strand.printStackTrace(st, System.err);
                assertThat(st[0].getMethodName(), equalTo("park"));
                boolean found = false;
                for (StackTraceElement ste : st) {
                    if (ste.getMethodName().equals("foo")) {
                        found = true;
                        break;
                    }
                }
                assertThat(found, is(true));
                assertThat(st[st.length - 1].getMethodName(), equalTo("run"));
                assertThat(st[st.length - 1].getClassName(), equalTo(Fiber.class.getName()));
            }
        }).start();

        fiber2.join();

        flag.set(true);
        cond.signalAll();

        fiber.join();
    }

    @Test
    public void testDumpStackSleepingFiber() throws Exception {
        // sleep is a special case
        Fiber fiber = new Fiber(scheduler, new SuspendableRunnable() {
            @Override
            public void run() throws SuspendExecution, InterruptedException {
                foo();
            }

            private void foo() throws InterruptedException, SuspendExecution {
                Fiber.sleep(1000);
            }
        }).start();

        Thread.sleep(200);

        StackTraceElement[] st = fiber.getStackTrace();

        // Strand.printStackTrace(st, System.err);
        assertThat(st[0].getMethodName(), equalTo("sleep"));
        boolean found = false;
        for (int i = 0; i < st.length; i++) {
            if (st[i].getMethodName().equals("foo")) {
                found = true;
                break;
            }
        }
        assertThat(found, is(true));
        assertThat(st[st.length - 1].getMethodName(), equalTo("run"));
        assertThat(st[st.length - 1].getClassName(), equalTo(Fiber.class.getName()));

        fiber.join();
    }

    @Test
    public void testBadFiberDetection() throws Exception {
        Fiber good = new Fiber("good", scheduler, new SuspendableRunnable() {
            @Override
            public void run() throws SuspendExecution, InterruptedException {
                for (int i = 0; i < 100; i++)
                    Strand.sleep(10);
            }
        }).start();

        Fiber bad = new Fiber("bad", scheduler, new SuspendableRunnable() {
            @Override
            public void run() throws SuspendExecution, InterruptedException {
                final long deadline = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(1000);
                for (;;) {
                    if (System.nanoTime() >= deadline)
                        break;
                }
            }
        }).start();

        good.join();
        bad.join();
    }

    @Test
    public void testUncaughtExceptionHandler() throws Exception {
        final AtomicReference<Throwable> t = new AtomicReference<>();

        Fiber<Void> f = new Fiber<Void>() {

            @Override
            protected Void run() throws SuspendExecution, InterruptedException {
                throw new RuntimeException("foo");
            }
        };
        f.setUncaughtExceptionHandler(new Strand.UncaughtExceptionHandler() {

            @Override
            public void uncaughtException(Strand f, Throwable e) {
                t.set(e);
            }
        });

        f.start();

        try {
            f.join();
            fail();
        } catch (ExecutionException e) {
            assertThat(e.getCause().getMessage(), equalTo("foo"));
        }

        assertThat(t.get().getMessage(), equalTo("foo"));
    }

    @Test
    public void testDefaultUncaughtExceptionHandler() throws Exception {
        final AtomicReference<Throwable> t = new AtomicReference<>();

        Fiber<Void> f = new Fiber<Void>() {

            @Override
            protected Void run() throws SuspendExecution, InterruptedException {
                throw new RuntimeException("foo");
            }
        };
        Fiber.setDefaultUncaughtExceptionHandler(new Strand.UncaughtExceptionHandler() {

            @Override
            public void uncaughtException(Strand f, Throwable e) {
                t.set(e);
            }
        });

        f.start();

        try {
            f.join();
            fail();
        } catch (ExecutionException e) {
            assertThat(e.getCause().getMessage(), equalTo("foo"));
        }
        final Throwable th = t.get();
       
        assertTrue(th != null);
        assertThat(th.getMessage(), equalTo("foo"));
    }

    @Test
    public void testUtilsGet() throws Exception {
        final List<Fiber<String>> fibers = new ArrayList<>();
        final List<String> expectedResults = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            final int tmpI = i;
            expectedResults.add("testUtilsSequence-" + tmpI);
            fibers.add(new Fiber<>(new SuspendableCallable<String>() {
                @Override
                public String run() throws SuspendExecution, InterruptedException {
                    return "testUtilsSequence-" + tmpI;
                }
            }).start());
        }

        final List<String> results = FiberUtil.get(fibers);
        assertThat(results, equalTo(expectedResults));
    }

    @Test
    public void testUtilsGetWithTimeout() throws Exception {
        final List<Fiber<String>> fibers = new ArrayList<>();
        final List<String> expectedResults = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            final int tmpI = i;
            expectedResults.add("testUtilsSequence-" + tmpI);
            fibers.add(new Fiber<>(new SuspendableCallable<String>() {
                @Override
                public String run() throws SuspendExecution, InterruptedException {
                    return "testUtilsSequence-" + tmpI;
                }
            }).start());
        }

        final List<String> results = FiberUtil.get(1, TimeUnit.SECONDS, fibers);
        assertThat(results, equalTo(expectedResults));
    }

    @Test(expected = TimeoutException.class)
    public void testUtilsGetZeroWait() throws Exception {
        final List<Fiber<String>> fibers = new ArrayList<>();
        final List<String> expectedResults = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            final int tmpI = i;
            expectedResults.add("testUtilsSequence-" + tmpI);
            fibers.add(new Fiber<>(new SuspendableCallable<String>() {
                @Override
                public String run() throws SuspendExecution, InterruptedException {
                    return "testUtilsSequence-" + tmpI;
                }
            }).start());
        }

        final List<String> results = FiberUtil.get(0, TimeUnit.SECONDS, fibers);
        assertThat(results, equalTo(expectedResults));
    }

    @Test(expected = TimeoutException.class)
    public void testUtilsGetSmallWait() throws Exception {
        final List<Fiber<String>> fibers = new ArrayList<>();
        final List<String> expectedResults = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            final int tmpI = i;
            expectedResults.add("testUtilsSequence-" + tmpI);
            fibers.add(new Fiber<>(new SuspendableCallable<String>() {
                @Override
                public String run() throws SuspendExecution, InterruptedException {
                    // increase the sleep time to simulate data coming in then timeout
                    Strand.sleep(tmpI * 3, TimeUnit.MILLISECONDS);
                    return "testUtilsSequence-" + tmpI;
                }
            }).start());
        }

        // must be less than 60 (3 * 20) or else the test could sometimes pass.
        final List<String> results = FiberUtil.get(55, TimeUnit.MILLISECONDS, fibers);
        assertThat(results, equalTo(expectedResults));
    }

    @Test
    public void testSerialization1() throws Exception {
        // com.esotericsoftware.minlog.Log.set(1);
        final SettableFuture<byte[]> buf = new SettableFuture<>();

        Fiber<Integer> f1 = new SerFiber1(scheduler, new SettableFutureFiberWriter(buf)).start();
        Fiber<Integer> f2 = Fiber.unparkSerialized(buf.get(), scheduler);

        assertThat(f2.get(), is(55));
    }

    static class SerFiber1 extends SerFiber<Integer> {
        public SerFiber1(FiberScheduler scheduler, FiberWriter fiberWriter) {
            super(scheduler, fiberWriter);
        }

        @Override
        public Integer run() throws SuspendExecution, InterruptedException {
            int sum = 0;
            for (int i = 1; i <= 10; i++) {
                sum += i;
                if (i == 5) {
                    Fiber.parkAndSerialize(fiberWriter);
                    assert i == 5 && sum == 15;
                }
            }
            return sum;
        }
    }

    @Test
    public void testSerialization2() throws Exception {
        // com.esotericsoftware.minlog.Log.set(1);
        final SettableFuture<byte[]> buf = new SettableFuture<>();

        Fiber<Integer> f1 = new SerFiber2(scheduler, new SettableFutureFiberWriter(buf)).start();
        Fiber<Integer> f2 = Fiber.unparkSerialized(buf.get(), scheduler);

        assertThat(f2.get(), is(55));
    }

    static class SerFiber2 extends Fiber<Integer> {
        public SerFiber2(FiberScheduler scheduler, final FiberWriter fiberWriter) {
            super(scheduler, new SuspendableCallable<Integer>() {

                @Override
                public Integer run() throws SuspendExecution, InterruptedException {
                    int sum = 0;
                    for (int i = 1; i <= 10; i++) {
                        sum += i;
                        if (i == 5) {
                            Fiber.parkAndSerialize(fiberWriter);
                            assert i == 5 && sum == 15;
                        }
                    }
                    return sum;
                }
            });
        }
    }

    @Test
    public void testSerializationWithThreadLocals() throws Exception {
        final ThreadLocal<String> tl1 = new ThreadLocal<>();
        final InheritableThreadLocal<String> tl2 = new InheritableThreadLocal<>();
        tl1.set("foo");
        tl2.set("bar");

        final SettableFuture<byte[]> buf = new SettableFuture<>();

        Fiber<Integer> f1 = new SerFiber3(scheduler, new SettableFutureFiberWriter(buf), tl1, tl2).start();
        Fiber<Integer> f2 = Fiber.unparkSerialized(buf.get(), scheduler);

        assertThat(f2.get(), is(55));
    }

    static class SerFiber3 extends SerFiber<Integer> {
        private final ThreadLocal<String> tl1;
        private final InheritableThreadLocal<String> tl2;

        public SerFiber3(FiberScheduler scheduler, FiberWriter fiberWriter, ThreadLocal<String> tl1, InheritableThreadLocal<String> tl2) {
            super(scheduler, fiberWriter);
            this.tl1 = tl1;
            this.tl2 = tl2;
        }

        @Override
        public Integer run() throws SuspendExecution, InterruptedException {
            assertThat(tl1.get(), is(nullValue()));
            assertThat(tl2.get(), is("bar"));

            tl1.set("koko");
            tl2.set("bubu");

            int sum = 0;
            for (int i = 1; i <= 10; i++) {
                sum += i;
                if (i == 5) {
                    Fiber.parkAndSerialize(fiberWriter);
                    assert i == 5 && sum == 15;
                }
            }

            assertThat(tl1.get(), is("koko"));
            assertThat(tl2.get(), is("bubu"));
            return sum;
        }
    }

    static class SerFiber<V> extends Fiber<V> implements java.io.Serializable {
        protected final transient FiberWriter fiberWriter;

        public SerFiber(FiberScheduler scheduler, SuspendableCallable<V> target, FiberWriter fiberWriter) {
            super(scheduler, target);
            this.fiberWriter = fiberWriter;
        }

        public SerFiber(FiberScheduler scheduler, FiberWriter fiberWriter) {
            super(scheduler);
            this.fiberWriter = fiberWriter;
        }
    }

    static class SettableFutureFiberWriter implements FiberWriter {
        private final transient SettableFuture<byte[]> buf;

        public SettableFutureFiberWriter(SettableFuture<byte[]> buf) {
            this.buf = buf;
        }

       
        @Override
        public void write(Fiber fiber, ByteArraySerializer ser) {
            buf.set(ser.write(fiber));
        }
       
//        @Override
//        public void write(byte[] serFiber) {
//            buf.set(serFiber);
//        }
    }
}
TOP

Related Classes of co.paralleluniverse.fibers.FiberTest$SerFiber

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');