Thanks to visit codestin.com
Credit goes to github.com

Skip to content

short Link Test

CoderDream edited this page Apr 29, 2022 · 1 revision
package com.coderdream.common;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ResultTest {

    @Test
    public void testResult() {
        Assert.assertEquals("Failure", ResultBuilder.buildFailure("Failure").getCode());
        Assert.assertEquals("200", ResultBuilder.buildFailure("200", "").getCode());
        Assert.assertEquals(true, ResultBuilder.buildSuccess().getSuccess());
        Assert.assertEquals("success", ResultBuilder.buildSuccess("success").getData());
        Assert.assertEquals(0, ResultBuilder.buildSuccessList(new ArrayList<>()).getData().size());
    }
}
package com.coderdream.helper;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@RunWith(SpringRunner.class)
@SpringBootTest
public class BloomFilterHelperTest {

    @Resource
    private BloomFilterHelper bloomFilterHelper;

    @Test
    public void testCache() {
        assertFalse(bloomFilterHelper.mightContain("123"));
        bloomFilterHelper.put("123");
        assertEquals(true, bloomFilterHelper.mightContain("123"));
    }
}
package com.coderdream.helper;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@RunWith(SpringRunner.class)
@SpringBootTest
public class FileOperateHelperTest {
    @Resource
    private FileOperateHelper fleOperateHelper;

    @Test
    public void testReadFile() {
        Assert.assertEquals("", fleOperateHelper.readFile("12"));
        Assert.assertEquals("6", fleOperateHelper.readFile("machineId"));
    }

    @Test
    public void testWriteFile() {
        fleOperateHelper.writeFile("test.txt", "test");
        Assert.assertEquals("test", fleOperateHelper.readFile("info/test.txt"));
    }
}
package com.coderdream.helper;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;


@RunWith(SpringRunner.class)
@SpringBootTest
public class GuavaCacheHelperTest {

    @Resource
    private GuavaCacheHelper guavaCacheHelper;

    @Test
    public void testCache() {
        assertNull(guavaCacheHelper.get(null));
        guavaCacheHelper.put("123", "test");
        assertEquals("test", guavaCacheHelper.get("123"));
    }
}
package com.coderdream.service;

import com.coderdream.helper.FileOperateHelper;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

@RunWith(SpringRunner.class)
@SpringBootTest
public class LinkServiceTest {
    @Resource
    private LinkService linkService;

    @Resource
    private FileOperateHelper fileOperateHelper;

    @Test
    public void testGetShortLink() {
        String shortLink = linkService.getShortLink("http://www.baidu.com/jLFYW7ZlML-344410");
        Assert.assertNotNull(shortLink);
        Assert.assertEquals("http://www.baidu.com/jLFYW7ZlML-344410", linkService.getLongLink(shortLink));
    }

    @Test
    public void testHashConflictLink() {
        String longLink1 = "http://www.baidu.com/8rkOPoKckoYowqylvIqMxG1V9HuUFJ-444728";
        String longLink2 = "http://www.baidu.com/twh2QwsOft5iard4aC8SPYvKulQy6C-921133";

        String shortLink1 = linkService.getShortLink(longLink1);
        String shortLink2 = linkService.getShortLink(longLink2);
        Assert.assertNotNull(shortLink1);
        Assert.assertEquals(longLink1, linkService.getLongLink(shortLink1));
        Assert.assertNotNull(shortLink2);
        Assert.assertEquals(longLink2, linkService.getLongLink(shortLink2));
    }

    @Test
    public void testStoreSameLink() {
        String longLink1 = "http://www.baidu.com/twh2QwsOft5iard4aC8SPYvKulQy6C-921133";
        String longLink2 = "http://www.baidu.com/twh2QwsOft5iard4aC8SPYvKulQy6C-921133";

        String shortLink1 = linkService.getShortLink(longLink1);
        String shortLink2 = linkService.getShortLink(longLink2);
        Assert.assertNotNull(shortLink1);
        Assert.assertEquals(longLink1, linkService.getLongLink(shortLink1));
        Assert.assertNotNull(shortLink2);
        Assert.assertEquals(longLink2, linkService.getLongLink(shortLink2));
        Assert.assertEquals(shortLink1, shortLink2);
        Assert.assertEquals(longLink1, longLink2);
    }


    /**
     * 预计要插入多少数据
     */
    private static int size = 1000000;

    /**
     * 期望的误判率
     */
    private static double fpp = 0.01;

    @Test
    public void testBloomFilterError() throws Exception {
        /**
         * 布隆过滤器
         */
        BloomFilter<String> filter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), size, fpp);

        String filePath = System.getProperty("user.dir") + "\\src\\test\\resources\\data.txt";

        // BufferedReader是可以按行读取文件
        FileInputStream inputStream = new FileInputStream(filePath);
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        List<String> list = new ArrayList<>();
        Set<String> set = new HashSet<>();
        String longLink = null;
        while ((longLink = bufferedReader.readLine()) != null) {
            System.out.println("longLink: " + longLink);
            String code = linkService.getShortLink(longLink);
          //  list.add(code);
            set.add(code);
        }

        System.out.println("list size: " + list.size() + " ; set size: " + set.size());
        //close
        inputStream.close();
        bufferedReader.close();
        list.addAll(set);
        // 99948
        // int size1 = 190000;
        // int size2 = 10000;
        int size1 = 88948;
        int size2 = 10000;

        // 插入19万样本数据
        for (int i = 0; i < size1; i++) {
            filter.put(list.get(i));
        }
        // 用另外1万测试数据,测试误判率
        int count = 0;
        for (int i = size1; i < size1 + size2; i++) {
            if (filter.mightContain(list.get(i))) {
                count++;
                System.out.println(list.get(i) + "误判了");
            }
        }
        System.out.println("总共的误判数:" + count);
    }

}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.coderdream</groupId>
    <artifactId>short-link</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>short-link</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <jacoco.version>0.8.3</jacoco.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.20</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>27.0.1-jre</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>${jacoco.version}</version>
                <configuration>
                    <excludes>
                        <exclude>**/bean/**</exclude>
                        <exclude>**/controller/**</exclude>
                        <exclude>**/domain/impl/CounterShortLinkHandler.*</exclude>
                        <exclude>**/utils/Config.*</exclude>
                        <exclude>**/utils/Constants.*</exclude>
                        <exclude>**/common/Result.*</exclude>
                    </excludes>
                </configuration>
                <executions>
                    <execution>
                        <id>default-prepare-agent</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>default-report</id>
                        <phase>test</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
package com.coderdream.service.impl;

import com.coderdream.bean.ShortLinkBean;
import com.coderdream.helper.BloomFilterHelper;
import com.coderdream.helper.GuavaCacheHelper;
import com.coderdream.service.LinkService;
import com.coderdream.utils.Config;
import com.coderdream.utils.DuplicatedEnum;
import com.coderdream.utils.ShortLinkComponent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
@Slf4j
public class LinkServiceImpl implements LinkService {
    @Resource
    private Config config;

    @Resource
    private ShortLinkComponent shortLinkComponent;

    @Resource
    private GuavaCacheHelper guavaCacheHelper;

    @Resource
    private BloomFilterHelper bloomFilterHelper;

    @Override
    public String getShortLink(String longLink) {
        String code = shortLinkComponent.createShortLinkCode(longLink);

        ShortLinkBean shortLinkBean = new ShortLinkBean();
        // 通过布隆过滤器判断:如果不存在(100%正确),则直接放入缓存中
        if (!bloomFilterHelper.mightContain(code)) {
            shortLinkBean.setShortLink(code);
            shortLinkBean.setLongLink(longLink);
            shortLinkBean.setExpireTime(System.currentTimeMillis() + config.EXPIRE_SEC * 1000);
            guavaCacheHelper.put(code, shortLinkBean);
            // 把短链接放入布隆过滤器
            bloomFilterHelper.put(code);
        }
        // 如果存在(可能误判)
        else {
            // 从缓存中取对象
            ShortLinkBean oldShortLinkBean = (ShortLinkBean) guavaCacheHelper.get(code);
            // 如果不存在误判为存在,则直接将新的数据写入缓存中
            if (oldShortLinkBean == null) {
                // 记录日志
                log.error("布隆过滤器误判了: " + code + " 不存在");
                shortLinkBean.setShortLink(code);
                shortLinkBean.setLongLink(longLink);
                shortLinkBean.setExpireTime(System.currentTimeMillis() + config.EXPIRE_SEC * 1000);
                guavaCacheHelper.put(code, shortLinkBean);
                // 把短链接放入布隆过滤器
                bloomFilterHelper.put(code);
            }
            // 如果确实存在
            else {
                String oldLongLink = oldShortLinkBean.getLongLink();
                // 判断是否Hash冲突了(code相同,长链接url不同)
                if (code.equals(oldShortLinkBean.getShortLink()) && !longLink.equals(oldLongLink)) {
                    // 记录日志
                    log.error("Hash冲突, old and new code: " + code + "; old link: " + oldLongLink + " ; new link: "
                            + longLink);

                    String newLongLink = "";
                    // 第一轮新code、新link
                    if (!oldLongLink.startsWith(DuplicatedEnum.DUPLICATED.getKey()) && !oldLongLink.startsWith(
                            DuplicatedEnum.OHMYGOD.getKey())) {
                        if (!oldLongLink.startsWith(DuplicatedEnum.DUPLICATED.getKey())) {
                            // code加上枚举前缀后Hash
                            code = shortLinkComponent.createShortLinkCode(DuplicatedEnum.DUPLICATED.getKey() + "_" + code);
                            newLongLink = DuplicatedEnum.DUPLICATED.getKey() + "_" + longLink;
                        } else {
                            code = shortLinkComponent.createShortLinkCode(
                                    DuplicatedEnum.OHMYGOD.getKey() + "_" + code);
                            newLongLink = DuplicatedEnum.OHMYGOD.getKey() + "_" + longLink;
                        }
                    }
                    log.error("Hash冲突解决: new code: " + code + "; old link: " + oldShortLinkBean.getLongLink()
                            + " ; new link: " + newLongLink);
                    shortLinkBean.setShortLink(code);
                    shortLinkBean.setLongLink(newLongLink);
                    shortLinkBean.setExpireTime(System.currentTimeMillis() + config.EXPIRE_SEC * 1000);
                    guavaCacheHelper.put(code, shortLinkBean);
                    // 把短链接放入布隆过滤器
                    bloomFilterHelper.put(code);

                }
                // 未冲突,已存在数据,不做处理,既不放到缓存中,也不放到过滤器中
                else {
                    // 记录日志
                    log.info("已存在: code " + code + " ; longLink: " + longLink);
                }
            }
        }

        return code;
    }

    @Override
    public String getLongLink(String shortLink) {
        String longLink = "";
            // 从缓存中获取对象
        ShortLinkBean shortLinkBean = (ShortLinkBean) guavaCacheHelper.get(shortLink);
        if(shortLinkBean != null){
            longLink = shortLinkBean.getLongLink();
            // 如果不存在Hash冲突标记,则直接返回长链接
            if (!longLink.startsWith(DuplicatedEnum.DUPLICATED.getKey()) && !longLink.startsWith(
                DuplicatedEnum.OHMYGOD.getKey())) {
                return longLink;
            }
            // 否则去掉冲突标记后再返回
            else {
                longLink = longLink.replace(DuplicatedEnum.DUPLICATED + "_", "");
                longLink = longLink.replace(DuplicatedEnum.OHMYGOD + "_", "");
            }
        } else {
            log.error(shortLink + " 对应的url不存在!");
        }

        return longLink;
    }

}
package com.example.demo.guava;

import static org.assertj.core.api.Assertions.assertThat;

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

import lombok.extern.slf4j.Slf4j;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.UUID;
import java.util.List;
import java.util.Set;
import java.util.stream.IntStream;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class BloomFilterTest {

    //  private static int insertions = 10000000;
    private static int insertions = 10000;

    @Test
    public void test1() {
        BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), insertions,
            0.001);
        Set<String> sets = new HashSet<>(insertions);
        List<String> lists = new ArrayList<>(insertions);
        for (int i = 0; i < insertions; i++) {
            String uid = UUID.randomUUID().toString();
            //System.out.println("uid: " + uid);
            bloomFilter.put(uid);
            sets.add(uid);
            lists.add(uid);
        }
        int right = 0;
        int wrong = 0;
        for (int i = 0; i < 10000; i++) {
            String data = i % 100 == 0 ? lists.get(i / 100) : UUID.randomUUID().toString();
            if (bloomFilter.mightContain(data)) {
                if (sets.contains(data)) {
                    right++;
                    continue;
                }
                wrong++;
            }
        }
        System.out.println("right: " + right + " ; wrong: " + wrong);
        NumberFormat percentFormat = NumberFormat.getPercentInstance();
        percentFormat.setMaximumFractionDigits(2);
        float percent = (float) wrong / 9900;
        float bingo = (float) (9900 - wrong) / 9900;
        System.out.println("在 " + insertions + " 条数据中,判断 100 实际存在的元素,布隆过滤器认为存在的数量为:" + right);
        System.out.println("在 " + insertions + " 条数据中,判断 9900 实际不存在的元素,布隆过滤器误认为存在的数量为:" + wrong);
        System.out.println("命中率为:" + percentFormat.format(bingo) + ",误判率为:" + percentFormat.format(percent));
    }

    @Test
    public void test2() {
        BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), 500, 0.01);
        bloomFilter.put(1);
        bloomFilter.put(2);
        bloomFilter.put(3);

        assertThat(bloomFilter.mightContain(1)).isTrue();
        assertThat(bloomFilter.mightContain(2)).isTrue();
        assertThat(bloomFilter.mightContain(3)).isTrue();
        assertThat(bloomFilter.mightContain(100)).isFalse();
    }

    @Test
    public void test3() {
        BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), 5, 0.01);
        IntStream.range(0, 100_000).forEach(bloomFilter::put);

        assertThat(bloomFilter.mightContain(1)).isTrue();
        assertThat(bloomFilter.mightContain(2)).isTrue();
        assertThat(bloomFilter.mightContain(3)).isTrue();
        // assertThat(bloomFilter.mightContain(100)).isFalse();
    }

    @Test
    public void test4() {
        // int size = 1000000;
        BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, 0.01);

        for (int i = 0; i < size; i++) {
            bloomFilter.put(i);
        }

        int count = 0;
        int newSize = size + 100000;
        for (int i = size; i < newSize; i++) {
            System.out.println(i);
            if (bloomFilter.mightContain(i)) {
                count++;
                System.out.println(i + "误判了");
            }
        }
        System.out.println("总共的误判数:" + count);
    }

    /**
     * 预计要插入多少数据
     */
    private static int size = 1000000;

    /**
     * 期望的误判率
     */
    private static double fpp = 0.01;

    /**
     * 布隆过滤器
     */
    private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, fpp);

    @Test
    public void test5() {
        // 插入10万样本数据
        for (int i = 0; i < size; i++) {
            bloomFilter.put(i);
        }
        // 用另外十万测试数据,测试误判率
        int count = 0;
        for (int i = size; i < size + 100000; i++) {
            if (bloomFilter.mightContain(i)) {
                count++;
                System.out.println(i + "误判了");
            }
        }
        System.out.println("总共的误判数:" + count);
    }

    @Test
    public void test6() throws Exception {
        /**
         * 布隆过滤器
         */
        BloomFilter<String> filter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), size, fpp);

        String filePath = System.getProperty("user.dir") + "\\src\\test\\resources\\code.txt";

        //BufferedReader是可以按行读取文件
        FileInputStream inputStream = new FileInputStream(filePath);
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String longLink = null;
        List<String> list = new ArrayList<>();
        Set<String> set = new HashSet<>();
        while ((longLink = bufferedReader.readLine()) != null) {
            System.out.println("longLink: " + longLink);
            //list.add(longLink);
            set.add(longLink);
        }
        //close
        inputStream.close();
        bufferedReader.close();
        list.addAll(set);

        // int size1 = 290000;
        // int size2 = 10000;
        int size1 = 98948;
        int size2 = 1000;

        // 插入29万样本数据
        for (int i = 0; i < size1; i++) {
            filter.put(list.get(i));
        }
        // 用另外1万测试数据,测试误判率
        int count = 0;
        for (int i = size1; i < size1 + size2; i++) {
            if (filter.mightContain(list.get(i))) {
                count++;
                System.out.println(list.get(i) + "误判了");
            }
        }
        System.out.println("总共的误判数:" + count);
    }

}
Clone this wiki locally